1.第一种创建线程的方式:
继承Thread并重写run方法来定义线程要执行的任务
public class ThreadDemo1 {
public static void main(String[] args) {
Thread th1=new MyThread1();
Thread th2=new MyThread2();
/*
* 启动线程要指定start方法,而不是直接调用run方法。
* run方法时线程要执行的任务,当线程的start方法被调用后,
* 线程进入runnable状态,一旦获取cpu时间,run方法会自动被 调用
*/
th1.start();
th2.start();
}
}
/*
* 第一种创建线程有两个不足的地方
* 1:由于java是单继承,那么当继承了Thread后就无法再继承其他类
* 2.由于继承Thread后重写run方法规定了线程执行的任务,这就导致了线程与任务有一个必然的耦合关系,不利于线程的重用。
*/
class MyThread1 extends Thread{
public void run() {
for(int i=0;i<100000;i++)
System.out.println("你是谁呀");
}
}
class MyThread2 extends Thread{
public void run() {
for(int i=0;i<100000;i++)
System.out.println("查水表的");
}
}
2.第二种创建线程的方式
实现Runnable接口并重写run方法
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("查水表的");
}
}
}
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("你是谁呀");
}
}
};
t1.start();
//方式二
Runnable runn=new Runnable() {
public void run() {
for(int i=0;i<1000;i++) {
System.out.println("查水表的");
}
}
};
Thread t2=new Thread(runn);
t2.start();
//同方式二
new Thread(new Runnable() {
public void run() {
for(int i=0;i<1000;i++) {
System.out.println("查水表的");
}
}
}).start();
}
}
4.Thread的API方法
static Thread currentThread():获取运行当前方法的线程
public class ThreadDemo4 {
public static void main(String[] args) {
Thread main=Thread.currentThread();
System.out.println("运行的线程是:"+main);
dosome();
Thread t1=new Thread() {
public void run() {
Thread t=Thread.currentThread();
System.out.println("自定义线程:"+t);
}
};
t1.start();
}
public static void dosome() {
Thread t=Thread.currentThread();
System.out.println("运行dosome的方法是:"+t);
}
}
package day09;
/**
* 获取线程相关信息
* long getId():返回该线程的标识符
* String getName():返回该线程的名称
* int getPriority():返回线程的优先级
* Thread.state getState():获取线程的状态
* boolean isAlive():测试线程是否处于活动状态
* boolean isDaemon():测试线程是否为守护线程
* @author LvChaoZhang
*
*/
public class ThreadDemo5 {
public static void main(String[] args) {
//获取运行main方法的线程
Thread main=Thread.currentThread();
long id=main.getId();
System.out.println("id:"+id);
String name=main.getName();
System.out.println("name:"+name);
int priority=main.getPriority();
System.out.println("优先级:"+priority);
boolean isAlive=main.isAlive();
System.out.println("是否存活:"+isAlive);
boolean isDaemon=main.isDaemon();
System.out.println("是否为守护线程:"+isDaemon);
boolean isInterrupt=main.isInterrupted();
System.out.println("是否中断:"+isInterrupt);
}
}
5.线程优先级
线程的时间片分配完全听线程调度的。线程只能被被动的被分配的时间。但是可以通过提高线程的优先级来达到尽可能干预的目的。
public class ThreadDemo6 {
public static void main(String[] args) {
Thread min=new Thread() {
public void run() {
for(int i=0;i<1000;i++) {
System.out.println("min");
}
}
};
Thread max=new Thread() {
public void run() {
for(int i=0;i<1000;i++) {
System.out.println("max");
}
}
};
Thread norm=new Thread() {
public void run() {
for(int i=0;i<1000;i++) {
System.out.println("norm");
}
}
};
min.setPriority(Thread.MIN_PRIORITY);
max.setPriority(Thread.MAX_PRIORITY);
min.start();
norm.start();
max.start();
}
}
6.static void sleep(long ms)
线程提供的静态方法sleep可以使运行该方法的线程,进入阻塞状态的那个毫秒,超时后线程会自动回到RUNNABLE状态。
public class ThreadDemo7 {
public static void main(String[] args) {
/*
* 完成电子表功能
* 每秒输出一次当前系统时间
*/
SimpleDateFormat sdf=new SimpleDateFormat("HH:mm:ss");
while(true) {
System.out.println(sdf.format(new Date()));
try {
Thread.sleep(1000);
}catch(Exception e) {
e.printStackTrace();
}
}
}
}
7.守护线程
又称后台线程,当一个线程中的所有前台线程都结束时,进程就要结束,若还有后台线程运行,那么后台线程就会被强制结束。
public class ThreadDemo8 {
public static void main(String[] args) {
Thread ros=new Thread() {
public void run() {
for(int i=0;i<10;i++) {
System.out.println("rose: let me go");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("ros:i jump!!!");
}
};
Thread jack=new Thread() {
public void run() {
while(true) {
System.out.println("jack:you jump,i jump");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
//设置为后台进程
jack.setDaemon(true);
ros.start();
jack.start();
System.out.println("main方法结束了");
//while(true);//加上这一句可以看到main方法时前台进程,main方法一直运行,所以后台程序不停
}
}
8.阻塞状态
void join()方法:join方法可以使调用该方法的线程进入阻塞状态,直到该方法所属线程完成工作后才会解除调用该方法线程的阻塞状态。join方法一般用来完成多个线程之间的同步工作问题。
public class ThreadDemo9 {
public static boolean isFinish=false;
public static void main(String[] args) {
final Thread downLoad=new Thread() {//这里的final注意jdk8之前需要加final 8之后就不需要了
public void run() {
System.out.println("down:开始下载图片");
for(int i=0;i<100;i++) {
System.out.println("down:"+i+"%");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
System.out.println("down:图片下载完成");
isFinish=true;
}
};
Thread show=new Thread() {
public void run() {
System.out.println("show:开始展示图片");
/*
* 先等待download把图片下载完毕!
*/
try {
downLoad.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
if(!isFinish) {
throw new RuntimeException("图片没有下载完成");
}
System.out.println("show:图片显示完毕");
}
};
downLoad.start();
show.start();
}
}
9.并发访问的解决措施
多线程并发访问同一资源时,就会形成"抢"的现象。由于线程切换时机不确定,可能导致执行代码顺序的混乱,严重时会导致系统瘫痪。
public class SynDemo1 {
public static void main(String[] args) {
Table table=new Table();
Thread t1=new Thread() {
public void run() {
while(true) {
int b1=table.getBean();
Thread.yield();
System.out.println(getName()+":"+b1);
}
}
};
Thread t2=new Thread() {
public void run() {
while(true) {
int b2=table.getBean();
Thread.yield();
System.out.println(getName()+":"+b2);
}
}
};
t1.start();
t2.start();
}
}
class Table{
private int beans=20;
/*
* 当一个方法被synchronized修饰后,该方法为同步方法,即:多个线程不能同时进入方法内部执行
*
* 对于成员方法而言,synchronized会在一个线程调用该方法所属对象加锁,其他线程在执行该方法时
* 由于执行方法的线程没有释放锁,所以只能在方法外阻塞,直到持有方法锁的线程将方法执行完毕。
*
* 所以,解决多线程并发执行安全问题的方法就是将“抢”变为“排队”。
*/
public synchronized int getBean() {
if(beans==0) {
throw new RuntimeException("没有豆子了");
}
Thread.yield();
return beans--;
}
}
10.同步块
/**
* 同步块
* 有效的缩小同步范围可以在保证并发安全的同时尽可能提高并发效率
* @author LvChaoZhang
*
*/
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 void buy() {
Thread t=Thread.currentThread();
try {
System.out.println(t.getName()+":"+"正在挑衣服");
Thread.sleep(5000);
/*
* 同步块可以要求多个线程对该块的代码排队执行,但是前提条件是同步监视对象
* 即(上锁的对象)要求多个线程看到的必须是同一个
*
* synchronized(同步监视器对象){
* 需要同步的代码
* }
* 所谓同步执行即:多个线程必须排队执行
* 所谓异步执行即:多个线程可以同时执行
*/
synchronized (this) {
System.out.println(t.getName()+":"+"正在试衣服");
Thread.sleep(5000);
}
System.out.println(t.getName()+":"+"正在结账");
} catch (Exception e) {
System.out.println("错误发生了");
}
}
}
11.同步组
/**
* 静态方法的同步
* 当一个静态方法被synchronized修饰后,那么该方法就为同步方法。
* 由于静态方法从属类,全局就一份,所以同步的静态方法一定具有同步效果,与对象无关
* @author LvChaoZhang
*
*/
public class SyncDdemo3{
public static void main(String[] args) {
Thread t1=new Thread() {
public void run() {
Foo.dosome();
}
};
Thread t2=new Thread() {
public void run() {
Foo.dosome();;
}
};
t1.start();
t2.start();
}
}
class Foo{
public synchronized static void dosome() {
try {
Thread t=new Thread();
System.out.println(t.getName()+":"+"正在运行");
Thread.sleep(5000);
System.out.println(t.getName()+":"+"运行结束");
}catch(Exception e) {
e.printStackTrace();
}
}
}
12.互斥锁
synchronized也叫互斥锁,即: 使用synchronized修饰多段代码,只要他们的同步监视器对象相同,那么这几段代码间就是互斥关系, 即:多个线程不能同时执行这些代码
/**
* 互斥锁
* synchronized也叫互斥锁,即:
* 使用synchronized修饰多段代码,只要他们的同步监视器对象相同,那么这几段代码间就是互斥关系,
* 即:多个线程不能同时执行这些代码
* @author LvChaoZhang
*
*/
public class SyncDemo4 {
public static void main(String[] args) {
final Boo boo=new Boo();
Thread t1=new Thread() {
public void run() {
boo.methodA();
}
};
Thread t2=new Thread() {
public void run() {
boo.methodB();
}
};
t1.start();
t2.start();
}
}
class Boo{
public synchronized void methodA() {
try {
Thread t=Thread.currentThread();
System.out.println(t.getName()+":"+"A方法正在执行");
Thread.sleep(5000);
System.out.println(t.getName()+":"+"A方法执行结束了");
}catch(Exception e) {
e.printStackTrace();
}
}
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) {
e.printStackTrace();
}
}
}
12.将集合或Map转换为线程安全的
/**
* 将集合或Map转换为线程安全的
* @author LvChaoZhang
*
*/
public class SynDemo5 {
public static void main(String[] args) {
/*
* ArrayList不是线程安全的
*/
List<String> list=new ArrayList<String>();
list.add("HELLO");
list.add("nihao");
list.add("你好");
System.out.println(list);
//将给定集合转换为线程安全的集合
list=Collections.synchronizedList(list);
System.out.println(list);
/*
* HashSet不是线程安全的
*/
Set<String> set=new HashSet<String>(list);
System.out.println(set);
//将给定的Set集合转换为线程安全的
set=Collections.synchronizedSet(set);
System.out.println(set);
/*
* HashMap也不是线程安全的
*/
Map<String,Integer> map=new HashMap<String,Integer>();
map.put("语文", 100);
map.put("数学",99);
map.put("英语", 98);
System.out.println(map);
//将给定的Map集合转换为线程安全的
map=Collections.synchronizedMap(map);
System.out.println(map);
/*
* API手册上有说明就算是线程安全的集合那么其中对于元素的操作,
* 如:add,remove等方法都不予迭代器遍历做互斥,需要自行维护互斥关系。
*/
}
}
13.线程池
/*
* 线程池
* 线程池主要有两个作用:
* 1.重用线程
* 2.控制线程数量
* 当我们的应用需要创建大量线程或者发现线程会频繁的创建和销毁时就应当考虑使用线程池来维护线程。
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService threadPool=Executors.newFixedThreadPool(2);
for(int i=0;i<5;i++) {
Runnable runn=new Runnable() {
public void run() {
Thread t=Thread.currentThread();
try {
System.out.println(t+":正在运行任务");
Thread.sleep(5000);
System.out.println(t+":运行任务结束");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("线程被中断了");
}
}
};
threadPool.execute(runn);
System.out.println("指派了一个任务交给线程池了");
}
threadPool.shutdownNow();
System.out.println("停止线程池了");
}
}