五、同步方法:
1、同步方法:首先书写一个show()方法,用于将操作共享数据的代码块放入其中;其次在重写的run()方法中调用show()方法。
注意:同步方法只可以在实现线程(implements Runnable)的程序中使用;而继承(extends Thread)的线程中不可以,因为继承的方式中不能实现线程安全问题。
2、同步方法的可以用于的是—用实现的方式来实现线程。
class Window4 implements Runnable {
int ticket = 100;
public void run() {
while(true) {
show();
}
}
//用synchronized修饰方法,保证是线程同步。
public synchronized void show() {//需要被同步的代码放到show()方法里
if (ticket > 0) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket--);
}
}
}
public class TestWindow4 {
public static void main(String[] args) {
Window4 w=new Window4();
Thread t1=new Thread(w);
t1.setName("售票窗口1");
Thread t2=new Thread(w);
t2.setName("售票窗口2");
Thread t3=new Thread(w);
t3.setName("售票窗口3");
t1.start();
t2.start();
t3.start();
}
}
3、同步方法-不可以用于用继承的方式来实现线程的方法中。
五、互斥锁_单例之懒汉式的线程安全:
线程的同步的弊端:由于同一个时间只能由一个线程访问共享数据,效率变低了。
package com.atguigu.Thread2;
//关于懒汉式的线程安全问题:使用同步机制。
//对于一般的方法内,使用同步代码块,可以考虑使用this。
//对于静态方法而言,使用当前类本身充当锁。
class Singleton {
private Singleton() {
}
private static Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) {//四个人到达,如果此时instance为空,可以进入;如果后来的判断instance已经有了造好了,就不用等了,直接拿就行
// Singleton.class返回的是一个对象
synchronized (Singleton.class) { // 使用当前类本身来充当锁:Singleton.class
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
public class TestSingleton {
public static void main(String[] args) {
// TODO Auto-generated method stub
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
Class cla = Singleton.class;
}
}
六、线程同步的练习:
package com.atguigu.Thread2;
class Account{
double balance;//余额
public Account() {
}
//存钱:使用synchronized保证了线程安全性
public synchronized void deposit(double amt) {
balance+=amt;
/* try-catch的加的原因是使错误的效果更明显 */
try {
Thread.currentThread().sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+":"+balance);
}
}
class Customer extends Thread{
Account account;
public Customer(Account account) {
this.account=account;
}
public void run() {
for(int i=0;i<3;i++) {
account.deposit(1000);
}
}
}
public class TestAccount {
public static void main(String[] args) {
Account acct=new Account();
Customer c1=new Customer(acct);
Customer c2=new Customer(acct);
c1.setName("甲");
c2.setName("乙");
c1.start();
c2.start();
}
}
七、线程的死锁问题:
吃饭的时候桌子上由一双筷子,两个人每个人抢到了一支,他们都不愿意放弃彼此的那一支筷子给对方,造成死锁状态,谁都吃不了饭。
package com.atguigu.Thread2;
//死锁的问题:处理线程同步时容易出现
public class TestDeadLock {
static StringBuffer sb1=new StringBuffer();
static StringBuffer sb2=new StringBuffer();
public static void main(String[] args) {
new Thread() {
public void run() {
synchronized(sb1) {//一号饭人握住sb1筷子
//try-catch部分,是休眠的时间,这段时间很有可能,sb2筷子被人抢走
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sb1.append("A");
synchronized(sb2) {
sb2.append("B");
System.out.println(sb1);
System.out.println(sb2);
}
}
}
}.start();
new Thread() {
public void run() {
synchronized(sb2) {//二号饭者握住sb2筷子
//try-catch部分二号饭者休眠,另一支筷子sb1已经被一号饭者拥有。
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sb1.append("c");
synchronized(sb1) {
sb2.append("d");
System.out.println(sb1);
System.out.println(sb2);
}
}
}
}.start();
}
}
八、线程的通信:
1、实现二者的交替打印,使用线程的通信。
package com.atguigu.Thread2;
class Account{
double balance;//余额
public Account() {
}
//存钱:使用synchronized保证了线程安全性
public synchronized void deposit(double amt) {
notify();
balance+=amt;
/* try-catch的加的原因是使错误的效果更明显 */
try {
Thread.currentThread().sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+":"+balance);
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class Customer extends Thread{
Account account;
public Customer(Account account) {
this.account=account;
}
public void run() {
for(int i=0;i<3;i++) {
account.deposit(1000);
}
}
}
public class TestAccount {
public static void main(String[] args) {
Account acct=new Account();
Customer c1=new Customer(acct);
Customer c2=new Customer(acct);
c1.setName("甲");
c2.setName("乙");
c1.start();
c2.start();
}
}
九、生产者、消费者的问题
:
分析题目:
- 是否涉及到多线程的问题?是!生产者、消费者。
- 是否涉及到共享数据?有!考虑线程的安全。
- 此共享数据是谁?即为产品的数量。
- 是否涉及到线程的通信呢?存在着生产者与消费者的通信。
package guigu;
/*
* 分析题目:
1、 是否涉及到多线程的问题?是!生产者、消费者。
2、 是否涉及到共享数据?有!考虑线程的安全。
3、 此共享数据是谁?即为产品的数量。
4、 是否涉及到线程的通信呢?存在着生产者与消费者的通信。
* */
class Clerk{//店员
int product;
/* 有同步需要考虑产品的安全问题synchronized */
public synchronized void addProduct() {//生产产品
if(product>=20) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else {
product++;
System.out.println(Thread.currentThread().getName()+":生产了第"+product+"个产品");
notifyAll();//唤醒消费者
}
}
public synchronized void consumeProduct(){//消费产品
if(product<=0) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else {
System.out.println(Thread.currentThread().getName()+":消费了第"+product+"个产品");
product--;
notifyAll();//唤醒生产者
}
}
}
class Producer implements Runnable{//生产者
Clerk clerk;
public Producer(Clerk clerk) {
this.clerk=clerk;
}
public void run() {
System.out.println("生产者开始生产产品");
while(true) {
try {
Thread.currentThread().sleep(100);//让生产的节奏慢一点。
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
clerk.addProduct();
}
}
}
class Consumer implements Runnable{
Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk=clerk;
}
public void run() {
System.out.println("消费者消费产品");
while(true) {
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
clerk.consumeProduct();
}
}
}
public class TestProduceConsume {
public static void main(String[] args) {
Clerk clerk=new Clerk();
Producer p1=new Producer(clerk);
Consumer c1=new Consumer(clerk);
Thread t1=new Thread(p1);//一个生产者的线程
Thread t3=new Thread(p1);//再造一个生产者的线程
Thread t2=new Thread(c1);//一个消费者的线程
t1.setName("生产者");
t2.setName("消费者");
t3.setName("生产者2");
t1.start();
t2.start();
t3.start();
}
}