概述
java 多线程学习笔记------(3)线程同步
前提:
并发:同一个对象多个线程同时操作
处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象。 这时候,我们就需要用到“线程同步”。 线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。
由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题。为了保证数据在方法中被访问时的正确性,在访问时加入锁机制(synchronized),当一个线程获得对象的排它锁,独占资源, 其他线程必须等待,使用后释放锁即可。
存在以下问题:
• 一个线程持有锁会导致其它所有需要此锁的线程挂起;
• 在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时, 引起性能问题;
• 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先倒置,引起性能问题。
由于我们可以通过 private关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是synchronized关键字它包括两种用法synchronized方法synchronized块。
• 同步方法
public synchronized void method(int args) {}
synchronized方法控制对“成员变量|类变量”对象的访问:每个对象对应一把锁,每个synchronized方法都必须获得调用该方法 的对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占 该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获 得该锁,重新进入可执行状态。
缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。
• 同步块: synchronized (obj){ },obj称之为同步监视器
• obj可以是任何对象,但是推荐使用共享资源作为同步监视器
• 同步方法中无需指定同步监视器,因为同步方法的同步监视器是this即该对象本身,或class即类的模子
• 同步监视器的执行过程
• 第一个线程访问,锁定同步监视器,执行其中代码
• 第二个线程访问,发现同步监视器被锁定,无法访问
• 第一个线程访问完毕,解锁同步监视器
• 第二个线程访问,发现同步监视器未锁,锁定并访问
死锁:
多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个 或者多个线程都在等待对方释放资源,都停止执 行的情形。某一个同步块同时拥有“两个以上对 象的锁”时,就可能会发生“死锁”的问题。
有无synchronized比较
无synchronized,线程不安全
package Thread;
/**
* @author 赌徒
* 取票线程不安全
*/
public class UnsafeTest01 {
public static void main(String[] args) {
//一份资源
UnsafeWeb12306 web=new UnsafeWeb12306();
//多个代理
new Thread(web,"张三").start();
new Thread(web,"李四").start();
new Thread(web,"王二麻子").start();
}
}
class UnsafeWeb12306 implements Runnable{
//票数
private int ticketNums=10;
//结束条件
private boolean flag=true;
@Override
public void run() {
while(flag) {
test();
}
}
//线程不安全
public void test() {
if (ticketNums<0) {
flag=false;
return;
}
//模拟延时
try {
Thread.sleep(200);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
}
}
package Thread;
/**
* @author 赌徒
*取钱线程不安全
*/
public class UnsafeTest02 {
public static void main(String[] args) {
Account account=new Account("账户", 100);
Drawing zhang=new Drawing(account, 80, "张三");
Drawing li=new Drawing(account, 90, "李四");
new Thread(zhang).start();
new Thread(li).start();
}
}
//模拟账户
class Account {
String name;
int money;
public Account(String name, int money) {
this.name = name;
this.money = money;
}
}
//模拟取钱
class Drawing extends Thread{
Account account;//取钱账户
int drawingmoney;//取钱数
int packetmoney;//口袋钱数
public Drawing(Account account, int drawingmoney,String name) {
super(name);
this.account = account;
this.drawingmoney = drawingmoney;
}
@Override
public void run() {
if (account.money-drawingmoney==0) {
return;
}
account.money-=drawingmoney;
packetmoney+=drawingmoney;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->账户余额"+account.money);
System.out.println(Thread.currentThread().getName()+"-->口袋余额"+packetmoney);
}
}
有synchronized,线程相对安全
package Thread;
/**
* @author 赌徒
*
* 线程安全:在并发时保证数据的正确性,效率尽可能高
*/
public class SynchronizedTest01 {
public static void main(String[] args) {
//一份资源
safeWeb12306 web=new safeWeb12306();
//多个代理
new Thread(web,"张三").start();
new Thread(web,"李四").start();
new Thread(web,"王二麻子").start();
}
}
class safeWeb12306 implements Runnable{
//票数
private int ticketNums=10;
//结束条件
private boolean flag=true;
@Override
public void run() {
while(flag) {
try {
Thread.sleep(200);
} catch (Exception e) {
// TODO: handle exception
}
test1();
}
}
//线程安全(同步)
public synchronized void test() {
if (ticketNums<=0) {
flag=false;
return;
}
//模拟延时
try {
Thread.sleep(200);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
}
//线程安全(同步块)
public void test1() {
if (ticketNums<=0) {//考虑没票
flag=false;
return;
}
synchronized(this) {
if (ticketNums<=0) {//考虑一张票
flag=false;
return;
}
//模拟延时
try {
Thread.sleep(200);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
}
}
}
package Thread;
/**
* @author 赌徒
*同步块
*/
public class SynchronizedTest02 {
public static void main(String[] args) {
Account1 account=new Account1("账户", 100);
Drawing1 zhang=new Drawing1(account, 80, "张三");
Drawing1 li=new Drawing1(account, 90, "李四");
zhang.start();
li.start();
}
}
//模拟账户
class Account1 {
String name;
int money;
public Account1(String name, int money) {
this.name = name;
this.money = money;
}
}
//模拟取钱
class Drawing1 extends Thread{
Account1 account;//取钱账户
int drawingmoney;//取钱数
int packetmoney;//口袋钱数
public Drawing1(Account1 account, int drawingmoney,String name) {
super(name);
this.account = account;
this.drawingmoney = drawingmoney;
}
@Override
public void run() {
if (account.money<=0) {
return;
}
//同步块
synchronized (account) {
if (account.money-drawingmoney<0) {
return;
}
account.money-=drawingmoney;
packetmoney+=drawingmoney;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->账户余额"+account.money);
System.out.println(Thread.currentThread().getName()+"-->口袋余额"+packetmoney);
}
}
}
死锁
package Thread;
/**
* 死锁:过多的同步可能造成相互不释放资源
* 从而相互等待,一般发生于同步中持有多个对象的锁
* @author 赌徒
* 解决:避免锁套锁
*
*/
public class DeadLock {
public static void main(String[] args) {
Markup markup1=new Markup(1, "张三");
Markup markup2=new Markup(0, "李四");
markup1.start();
markup2.start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
//化妆
class Markup extends Thread{
static Lipstick lipstick=new Lipstick();
static Mirror mirror=new Mirror();
//选择
int choice;
//名字
String nameString;
public Markup(int choice,String nameString) {
this.nameString=nameString;
this.choice = choice;
}
public void run() {
//化妆
markup();
}
//相互持有对方的对象锁:可能造成死锁
private void markup() {
if (choice==0) {
synchronized (lipstick) {//获得口红的锁
System.out.println(this.nameString+"获得口红");
//一秒后想获得镜子的锁
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// synchronized (mirror) {//获得镜子的锁
// System.out.println(this.nameString+"获得镜子");
// }
}
synchronized (mirror) {//获得镜子的锁
System.out.println(this.nameString+"获得镜子");
}
}else {
synchronized (mirror) {//获得镜子的锁
System.out.println(this.nameString+"获镜子");
//二秒后想获得口红的锁
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// synchronized (lipstick) {//获得口红的锁
// System.out.println(this.nameString+"获得口红");
// }
}
synchronized (lipstick) {//获得口红的锁
System.out.println(this.nameString+"获得口红");
}
}
}
}
最后
以上就是淡然书本为你收集整理的java 多线程学习笔记------(3)线程同步(并发控制)java 多线程学习笔记------(3)线程同步的全部内容,希望文章能够帮你解决java 多线程学习笔记------(3)线程同步(并发控制)java 多线程学习笔记------(3)线程同步所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复