class MyThread1 extends Thread{
public void run(){
for(int i=0;i<1000;i++){
System.out.println("你是谁啊?");
}
}
}
class MyThread2 extends Thread{
public void run(){
for(int i=0;i<1000;i++){
System.out.println("开门,查水表的!");
}
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
Thread t1 = new MyThread1();
Thread t2 = new MyThread2();
t1.start();
t2.start();
}
}
优点:结构简单,利于匿名内部类创建
缺点:
1)存在继承冲突问题。实际开发中我们通常使用继承用于复用某个类的方法,但是如果继承了线程则无法再继承其他类
2)当我们继承Thread后需要重写run方法来定义线程任务,这导致线程与任务存在了必然的耦合关系,不利于线程的复用。
class MyRunnable1 implements Runnable{
public void run(){
for(int i=0;i<1000;i++){
System.out.println("你是谁啊?");
}
}
}
class MyRunnable2 implements Runnable{
public void run(){
for(int i=0;i<1000;i++){
System.out.println("我是查水表的!");
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
Runnable r1 = new MyRunnable1();
Runnable r2 = new MyRunnable2();
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
public class ThreadDemo3 {
public static void main(String[] args) {
Thread t1 = new Thread(){
public void run(){
for (int i = 0; i < 1000; i++) {
System.out.println("你是谁啊?");
}
}
};
/*Runnable r2 = new Runnable(){
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println("我是查水表的!");
}
}
};
Thread t2 = new Thread(r2);*/
//Runnable接口可以使用lambda表达式创建
/*Runnable r2 = ()->{
for (int i = 0; i < 10; i++) {
System.out.println("我是查水表的!");
}
};
Thread t2 = new Thread(r2);*/
Thread t2 = new Thread(()->{
for (int i = 0; i < 10; i++) {
System.out.println("我是查水表的!");
}
});
t1.start();
t2.start();
}
}
Thread 提供的静态方法:
public class CurrentThreadDemo {
public static void main(String[] args) {
Thread main = Thread.currentThread();
System.out.println("主线程:"+main);
doSome();//主线程执行doSome方法
//自定义一个线程
Thread t = new Thread(){
public void run(){
Thread current = Thread.currentThread();
System.out.println("自定义线程:"+current);
doSome();//自定义线程执行doSome方法
}
};
t.start();
}
public static void doSome(){
//任何方法中都可以用CurrentThread获取当前线程
Thread t = Thread.currentThread();
System.out.println("运行doSome方法的线程是:"+t);
}
}
输出:
主线程:Thread[main,5,main]
运行doSome方法的线程是:Thread[main,5,main]
自定义线程:Thread[Thread-0,5,main]
运行doSome方法的线程是:Thread[Thread-0,5,main]
public class SleepDemo {
public static void main(String[] args) {
System.out.println("程序开始了");
try{
Thread.sleep(5000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("程序结束了");
}
}
倒计时程序:
public class SleepDemo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入倒计时:");
int time = scanner.nextInt();
for(int i=time;i>0;i--){
System.out.println(i);
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println("时间到");
}
}
当多个线程并发操作同一临界资源,由于线程切换时机不确定,导致操作顺序混乱从而引发不良后果
临界资源:操作该资源的全过程同一时刻只允许单线程进行
public class Bank {
private int account = 20000; //账户余额
//取款方法,返回值为true表示允许出钞,否则拒绝出钞
public boolean getMoney(int money){
int account = getAccount(); //获取当前余额
if(account>=money){
account -= money;
Thread.yield();//让执行yield方法的线程主动放弃本次剩余时间片,用于模拟执行到这里正好时间片耗尽
saveAccount(account);
return true;
}
return false;
}
//查询账户余额
public int getAccount(){
return account;
}
//保存新余额
public void saveAccount(int account){
this.account = account;
}
}
public class SyncDemo1 {
public static boolean success1=false;//t1是否取款成功
public static boolean success2=false;//t2是否取款成功
public static void main(String[] args) {
/*
为什么success1和success2不能直接定义在这里
因为java语法规定,当一个方法的局部内部类中如果引用了这个方法的其他局部变量
那么这个变量必须是final的,这意味着局部内部类中不能改变这个变量的值。
而我们的案例中,局部内部类t1线程或t2线程中需要改变success1或success2的值
因此它会违反final要求,因此不能定义为局部变量。
*/
//boolean success1=false;
Bank bank = new Bank();
while(true) {
//在方法中定义的内部类,称为局部内部类
Thread t1 = new Thread() {
public void run() {
success1 = bank.getMoney(20000);
}
};
Thread t2 = new Thread() {
public void run() {
success2 = bank.getMoney(20000);
}
};
t1.start();
t2.start();
try {
Thread.sleep(10);//让主线程卡10毫秒,目的是等待两个线程执行完
} catch (InterruptedException e) {
e.printStackTrace();
}
if (success1 && success2) {
System.out.println("bug卡成功!可刑可铐");
break;
} else {
bank.saveAccount(20000);
success1 = false;
success2 = false;
}
}
}
}
解决并发安全问题的本质就是将多个线程并发(同时)操作改为同步(排队)操作来解决。
synchronized有两种使用方式
同步方法:
当一个方法使用synchronized修饰后,这个方法称为"同步方法",即多个线程不能同时在方法内部执行,只能有先后顺序的一个一个进行, 将并发操作同一临界资源的过程改为同步执行就可以有效的解决并发安全问题
public synchronized boolean getMoney(int money)
同步块:
有效的缩小同步范围可以在保证并发安全的前提下尽可能的提高并发效率,同步块可以更准确的控制需要多个线程排队执行的代码片段
语法:
synchronized(同步监视器对象){
需要多线程同步执行的代码片段
}
同步监视器对象即上锁的对象,要想保证同步块中的代码被多个线程同步运行,则要求多个线程看到的同步监视器对象是同一个
public class SyncDemo2 {
public static void main(String[] args) {
Shop shop1 = new Shop();
Shop shop2 = new Shop();
Thread t1 = new Thread("张三"){
public void run(){
shop1.buy();
}
};
Thread t2 = new Thread("李四"){
public void run(){
shop2.buy();
}
};
t1.start();
t2.start();
}
}
class Shop{
//在成员方法上使用synchronized时,同步监视器对象不可选,只能是this
//public synchronized void buy(){}
public void buy(){
try{
Thread t = Thread.currentThread();
System.out.println(t.getName()+"正在挑衣服...");
Thread.sleep(5000);
/*
通常同步监视器对象选择:临界资源
换句话说:抢谁锁谁
*/
synchronized (this){
System.out.println(t.getName()+"正在试衣服...");
Thread.sleep(5000);
}
System.out.println(t.getName()+"结账离开");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
有效的缩小同步范围可以在保证并发安全的前提下提高并发效率
同步监视器对象的选取
互斥性
当使用多个synchronized修饰了多个代码片段,并且指定的同步监视器都是同一个对象时,这些代码片段就
是互斥的,多个线程不能同时在这些代码片段上执行
当在静态方法上使用synchronized后,该方法是一个同步方法.由于静态方法所属类,所以一定具有同步效果,静态方法使用的同步监视器对象为当前类的类对象(Class的实例)
静态方法中使用同步块时,指定的锁对象通常也是当前类的类对象
public class SyncDemo3 {
public static void main(String[] args) {
Boo b1 = new Boo();
Boo b2 = new Boo();
Thread t1 = new Thread(){
public void run(){
// Boo.doSome();
b1.doSome();
}
};
Thread t2 = new Thread(){
public void run(){
// Boo.doSome();
b2.doSome();
}
};
t1.start();
t2.start();
}
}
class Boo{
//静态方法指定的同步监视器对象不再是this,而是当前类的类对象(Class实例)
// public synchronized static void doSome(){}
public static void doSome(){
//获取类对象的方式:类名.class
synchronized (Boo.class){
Thread t = Thread.currentThread();
System.out.println(t+"正在执行doSome方法");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t+"执行doSome方法完毕");
}
}
}