1.线程的同步和通信
虚假唤醒:在increment和decrement里面需要用while 而不能用if,记住:在多线程中永远用while而不用if,因为if判断有可能这次通过,但是还有可能方法体内wait等待,所以必须用while。
class ShareData{
private int number = 0;
public synchronized void increment() throws InterruptedException{
while(0 != number ){
this.wait();
}
++ number;
System. out.println(Thread. currentThread().getName()+"\t"+ number);
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException{
while(0 == number ){
this.wait();
}
-- number;
System. out.println(Thread. currentThread().getName()+"\t"+ number);
this.notifyAll();
}
}
/**
* 题目:现在两个线程,可以操作同一个变量,实现一个线程对该变量加1,一个线程对该变量减1,实现交替,来10轮,变量初始值为零。
* @author admin
* 1 高内聚低耦合
* 2 线程 操作 资源
*/
public class ThreadDemo2{
public static void main(String[] args){
final ShareData shareData = new ShareData();
new Thread(new Runnable(){
@Override
public void run()
{
for (int i = 0; i < 10; i++){
try{
shareData.increment();
Thread. sleep(200);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}, "AA").start();
new Thread(new Runnable()
{
@Override
public void run()
{
for (int i = 0; i < 10; i++)
{
try
{
shareData.decrement();
Thread. sleep(300);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}, "BB").start();
new Thread(new Runnable()
{
@Override
public void run()
{
for (int i = 0; i < 10; i++)
{
try
{
shareData.increment();
Thread. sleep(400);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}, "CC").start();
new Thread(new Runnable()
{
@Override
public void run()
{
for (int i = 0; i < 10; i++)
{
try
{
shareData.decrement();
Thread. sleep(500);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}, "DD").start();
}
}
2.加同步方法,加静态同步方法练习
//1 标准访问,先打印苹果还是android
//2 加入Thread.sleep(4000),先打印苹果还是android?
//3 加入getHello(),先打印苹果还是hello?
//4 有两部手机,先打印苹果还是android?
//5 两个静态同步方法,有一部手机,先打印苹果还是android?
//6 两个静态同步方法,有两部手机,先打印苹果还是android?
//7 一个静态同步方法,一个普通同步方法,有一部手机,先打印苹果还是android?
//先打印ios,因为在ios锁的是类对象本身 而android锁的是实例对象本身
//8 一个静态同步方法,一个普通同步方法,有两部手机,先打印苹果还是android?
public class ThreadDemo3{
public static void main(String[] args){
final Phone phone = new Phone();
final Phone phone2 = new Phone();
new Thread(new Runnable(){
@Override
public void run(){
for (int i = 0; i < 1; i++){
try{
phone.getIOS ();
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}, "AA").start();
new Thread(new Runnable(){
@Override
public void run(){
for (int i = 0; i < 1; i++){
try
{
//phone.getAndroid();
//phone.getHello();
phone2.getAndroid();
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}, "BB").start();
}
}
class Phone{
public static synchronized void getIOS() throws InterruptedException{
Thread.sleep(4000);
System.out.println("-----getIOS" );
}
public synchronized void getAndroid() throws InterruptedException{
System.out.println("-----getAndroid" );
}
public void getHello() throws InterruptedException{
System.out.println("-----getHello" );
}
}
结论:
一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一synchronized方法了,其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法
锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
加个普通方法后发现和同步锁无关
换成两个对象后,不是同一把锁了,情况立刻变化。
都换成静态同步方法后,情况又变化
所有的非静态同步方法用的都是同一把锁——实例对象本身,也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
所有的静态同步方法用的也是同一把锁——类对象本身,这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!
3.考察两个线程相互交换:在main方法中创建并启动两个线程。第一个线程循环随机打印100以内的整数,直到第二个线程从键盘读取了“Q”命令。
public class HelloThread4 implements Runnable{
private static boolean flag = true;
@Override
public void run() {
while(flag){
int num = (int)(Math.random() * 101);
System.out.println(num);
}
}
public static void setFlag(){
flag = false;
}
}
public class HelloThread5 implements Runnable{
@Override
public void run() {
Scanner scan = new Scanner(System.in);
while(scan.hasNext()){
String str = scan.next();
if(str.equalsIgnoreCase("Q")){
HelloThread4.setFlag();
System.out.println("---------------------------------");
}
}
}
}
/*
* 在main方法中创建并启动两个线程。第一个线程循环随机打印100以内的整数,直到第二个线程从键盘读取了“Q”命令。
*/
public class TestThread2 {
public static void main(String[] args) {
HelloThread4 ht4 = new HelloThread4();
Thread t1 = new Thread(ht4);
t1.start();
HelloThread5 ht5 = new HelloThread5();
Thread t2 = new Thread(ht5);
t2.start();
}
}
4.考察线程基本使用:编写程序,在main方法中创建一个线程。线程每隔一定时间(200ms以内的随机时间)产生一个0-100之间的随机整数,打印后将该整数放到集合中; 共产生100个整数,全部产生后,睡眠30秒,然后将集合内容打印输出; 在main线程中,唤醒上述睡眠的线程,使其尽快打印集合内容。
public class HelloThread6 implements Runnable{
List<Integer> list = new ArrayList<Integer>();
static boolean flag = false;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
int millis = (int)(Math.random() * 200);
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
}
int num = (int)(Math.random() * 101);
System.out.println(num);
list.add(num);
}
//-------------------
flag = true;
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
}
Iterator<Integer> it = list.iterator();
while(it.hasNext()){
System.out.println("--" + it.next());
}
}
}
public class TestThread3 {
public static void main(String[] args) {
HelloThread6 ht6 = new HelloThread6();
Thread t6 = new Thread(ht6);
t6.start();
while(t6.isAlive()){
if(HelloThread6.flag){
t6.interrupt();
}
}
}
}
5.线程同步解决线程单利设计模式的懒汉式线程安全问题
public class TestSingleton {
}
//懒汉式
class Singleton{
//1. 私有化构造器
private Singleton(){}
//2. 类的内部创建对象
private static Singleton instance = null;
public static Singleton getInstance(){
if(instance == null){//为了减少判断,可用于提高效率
synchronized (Singleton.class) {//Class
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}