1.创建线程的方式
1.1继承Thread类并重写run方法
public class ThreadDemo1 {
public static void main(String[] args) {
MyThread1 t1 = new MyThread1();
MyThread2 t2 = new MyThread2();
/*
* 启动线程要调用start方法,而不是run方法
*/
t1.start();
t2.start();
}
}
/**
* 第一种创建线程的方式的优点是定义简单,适合匿名内部类形式吃创建。
* 缺点主要有两个:
* 1:java是单继承的,这导致继承了Thread就无法再继承其他类去复用方法了,这在实际开发时很不方便。
* 2:将任务定义在线程中,会导致线程和任务存在必然的耦合关系,不利于线程的重用
* @author 张庆裕
*
*/
class MyThread1 extends Thread{
public void run() {
for(int i=0;i<100;i++) {
System.out.println("你是谁");
}
}
}
class MyThread2 extends Thread{
public void run() {
for(int i =0;i<100;i++) {
System.out.println("我是张三");
}
}
}
1.2实现Runnable接口单独定义线程任务
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();
}
}
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("我是张三!");
}
}
}
1.3使用匿名内部类形式创建线程
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 = ()->{
for (int i = 0; i < 1000; i++) {
System.out.println("我是张三!");
}
};
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
}
2.线程的优先级
/**
* 线程的优先级
*
* 线程获取CPU的时间片并发运行时,一切听从线程调度,线程是不能主动索取时间片的
* 只能被动被分配。
* 调整线程的优先级可以最大限度的干涉分配时间片的概率,原则上优先级越高的线程获取CPU时间片的次数越多。
* @author 张庆裕
*
*/
public class PriorityDemo {
public static void main(String[] args) {
Thread max = new Thread() {
public void run() {
for(int i=0;i<10000;i++) {
System.out.println("max");
}
}
};
Thread norm = new Thread() {
public void run() {
for(int i=0;i<10000;i++) {
System.out.println("nor");
}
}
};
Thread min = new Thread() {
public void run() {
for(int i=0;i<10000;i++) {
System.out.println("min");
}
}
};
//线程优先级1最低,5默认,10最高
max.setPriority(Thread.MAX_PRIORITY);
norm.setPriority(Thread.NORM_PRIORITY);
min.setPriority(Thread.MIN_PRIORITY);
max.start();
norm.start();
min.start();
}
}
3.线程阻塞
3.1 Thread.Sleep(long ms)
/**
* static void Sleep(long ms)
* Thread提供的静态方法sleep可以让运行该方法的线程阻塞指定毫秒,超时后线程会自动
* 回到RUNNABLE状态,等待再次获取时间片并发运行。
* @author 张庆裕
*
*/
public class SleepDemo {
public static void main(String[] args) {
System.out.println("程序开始了");
/*
*实现一个倒计时程序,程序启动后要求输入一个整数,然后每秒递减,到0时输出时间到
*/
Scanner scan = new Scanner(System.in);
System.out.println("请输入一个整数:");
int num = scan.nextInt();
while(num>0) {
try {
System.out.println(num--);
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("时间到!");
System.out.println("程序结束了");
}
}
3.2 interrupt()方法中断其sleep阻塞
/**
* sleep方法要求我们必须处理InterruptedException,当一个线程调用sleep方法处于
* 阻塞的过程中,此时该线程的interrupt()方法被调用时会中断其sleep阻塞,此时该方法就会抛出这个异常
* @author 张庆裕
*
*/
public class SleepDemo2 {
public static void main(String[] args) {
Thread lin = new Thread() {
public void run() {
System.out.println("林:我要睡觉了,别吵我!");
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
System.out.println("林:干嘛嘞,干嘛嘞!");
}
System.out.println("林:我醒了");
}
};
Thread huang = new Thread() {
public void run() {
System.out.println("黄:开始砸墙!");
for(int i=0;i<5;i++) {
System.out.println("黄:80!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
}
System.out.println("黄:搞定了");
lin.interrupt();
}
};
lin.start();
huang.start();
}
}
4.线程的相关方法
public class ThreadInfoDemo {
public static void main(String[] args) {
Thread t = Thread.currentThread();
String name = t.getName();
System.out.println("线程名字:"+name);
long id = t.getId();
System.out.println("唯一标识:"+id);
int priority = t.getPriority();
System.out.println("优先级:"+priority);
//查看当前线程是否还活着
boolean isAlive = t.isAlive();
System.out.println("isAlive:"+isAlive);
//查看线程是否为守护线程
boolean isDaemon = t.isDaemon();
System.out.println("isDaemon:"+isDaemon);
//查看线程是否被中断了
boolean isInterrupted = t.isInterrupted();
System.out.println("isInterrupted:"+isInterrupted);
}
}
5.守护线程
/**
* 守护线程
* 守护线程是通过普通线程调用setDaemon方法设置而来的,因此创建和使用其实与普通
* 线程无异。但是结束时机上有一点不同,即进程的结束
*
* 进程结束:当进程中的所有普通线程都结束时,进程就会结束,此时所有正在运行的守护线程都会被强制中断
* @author 张庆裕
*
*/
public class DaemonThreadDemo {
public static void main(String[] args) {
Thread t1 = new MyCommon();
Thread t2 = new Thread(new MyDaemon());
t1.start();
t2.setDaemon(true); //设置为守护线程,必须在该线程启动前进行!
t2.start();
}
}
class MyCommon extends Thread {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"线程1第" + i + "次执行!");
try {
Thread.sleep(7);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyDaemon implements Runnable {
public void run() {
while (true){
System.out.println(Thread.currentThread().getName()+"后台线程第次执行!");
try {
Thread.sleep(7);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
6.线程池
/**
* 线程池
* 线程池是管理线程的一套解决方案,主要工作:
* 1:控制线程数量
* 线程数量过多会消耗大量内存,有可能引起内存溢出崩溃,并且线程数量过多会导致CPU的过度切换,从而降低整体并发性能。
* 2:重用线程
* 线程不应当与任务的生命周期一致,重复使用线程可以减少线程调度器的不必要开销。
* @author 张庆裕
*
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(2);
for(int i=0;i<5;i++) {
Runnable r = new Runnable() {
public void run() {
try {
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在执行任务...");
Thread.sleep(5000);
System.out.println(t.getName()+":执行任务完毕!");
} catch (Exception e) {
}
}
};
threadPool.execute(r);//将任务交给线程池
System.out.println("指派了一个任务给线程池...");
}
//threadPool.shutdownNow();//调用该方法线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;
System.out.println("停止线程池");
}
}
7.多线程并发安全问题
7.1.同步方法(synchronized)
/**
* 多线程并发安全问题
* 当多个线程并发操作同一临界资源时,由于线程切换的时机不确定,导致操作过程的顺序错乱,出现严重的后果
* 临界资源:同时只能被单个线程操作的资源。
* @author 张庆裕
*
*/
public class SyncDemo1 {
public static void main(String[] args) {
Table beans = new Table();
Thread t1 = new Thread() {
public void run() {
while(true) {
int bean = beans.getBean();
Thread.yield();
System.out.println(getName()+":"+bean);
}
}
};
Thread t2 = new Thread() {
public void run() {
while(true) {
int bean = beans.getBean();
Thread.yield();
System.out.println(getName()+":"+bean);
}
}
};
t1.start();
t2.start();
}
}
class Table{
private int beans = 20;
/**
* 当一个方法被synchronized修饰后,该方法称为同步方法,即:多个线程不能同时在方法
* 内部执行,将异步调用方法该为同步调用可以解决并发安全问题
* @return
*/
public synchronized int getBean(){
if(beans==0) {
throw new RuntimeException("没有豆子了!");
}
Thread.yield();//使当前线程由执行状态,变成为就绪状态,让出cpu时间,在下一个线程执行时候,此线程有可能被执行,也有可能没有被执行
return beans--;
}
}
7.2同步块
/**
* 同步块:
* 语法:
* synchronized(同步监视器对象){
* 需要多线程同步运行的代码片段
* }
*
* 同步块可以更准确的锁定需要同步运行的代码片段,有效的缩小同步范围可以在保证并发
* 安全的前提下提高并发效率
* @author 张庆裕
*
*/
public class SyncDemo2 {
public static void main(String[] args) {
Shop shop = new Shop();
Thread t1 = new Thread() {
public void run() {
shop.buy();
}
};
Thread t2 = new Thread() {
public void run() {
shop.buy();
}
};
t1.start();
t2.start();
}
}
class Shop{
//public synchronized void buy() {//没有任何同步约束时性能最好,但是存在并发安全问题
/*
* 在方法上使用synchronized,那么指定的同步对象监视器对象就是this
*/
public void buy() {//解决了并发安全问题 ,但是性能不好,原因是衣服可同时挑,试衣同一时间只能一个人试衣服
try {
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在挑衣服!");
Thread.sleep(5000);
/*
*同步块使用时要求必须指定同步监听器对象:()中的内容
*该对象可以是java中任何类型的实例。只需要保证一点:多个需要同步执行的线程看到的这个对象必须是同一个
*/
synchronized(this) {//同对象同一时间只能有一个线程进入
System.out.println(t.getName()+"正在试衣服!");
Thread.sleep(5000);
}
System.out.println(t.getName()+":结账离开!");
} catch (Exception e) {
}
}
}
7.3静态方法修饰synchronized
/**
* 成员方法上使用synchronized修饰后,锁对象为该方法所属对象this
* 但是静态方法不同,箭头方法所属类,全局就一份,因为静态方法上使用synchronized后
* 该方法一定具有同步效果,而它指定的锁对象为当前的类对象(Class的一个实例。)
* @author 张庆裕
*
*/
public class SyncDemo3 {
public static void main(String[] args) {
Boo b = new Boo();
Thread t1 = new Thread() {
public void run() {
Boo.dosome();
}
};
Thread t2 = new Thread() {
public void run() {
Boo.dosome();
}
};
t1.start();
t2.start();
}
}
class Boo{
public synchronized static void dosome() {
try {
synchronized (Boo.class) {
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在执行dosome方法");
Thread.sleep(5000);
System.out.println(t.getName()+"dosome方法执行完毕!");
}
} catch (Exception e) {
}
}
}
7.4互斥锁
/**
* 互斥锁
* 当使用多个synchronized锁定多个代码片段时,这些synchronized指定的同步监听器对象是同一个时,
* 那么这些代码片段就是互斥的,多个线程不能同时执行写几个代码片段。
* @author 张庆裕
*
*/
public class SyncDemo4 {
public static void main(String[] args) {
Foo f = new Foo();
Thread t1 = new Thread() {
public void run() {
f.methodA();
}
};
Thread t2 = new Thread() {
public void run() {
f.methodB();
}
};
t1.start();
t2.start();
}
}
class Foo{
public synchronized void methodA() {
/*
* 这样写仍然与下面的methodB方法有互斥效果,因为这里指定的同步监视器对象是this。
* 也就是nethodA方法所属对象,而methodB上直接写synchronized时指定的也是this
* 那么此时两个线程分别调用同一个Foo对象的methodA和methodB方法时就是互斥的
*/
// synchronized (this) {
//
// }
try {
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在执行A方法...");
Thread.sleep(5000);
System.out.println(t.getName()+"A方法执行完毕!");
} catch (Exception e) {
}
}
public synchronized void methodB() {
try {
Thread t = Thread.currentThread();
System.out.println(t.getName()+":正在执行B方法...");
Thread.sleep(5000);
System.out.println(t.getName()+"B方法执行完毕!");
} catch (Exception e) {
}
}
}