一、创建线程的方法
1.继承Thread
public class 继承Thread {
public static void main(String[] args){
T1 t1=new T1();
T2 t2=new T2();
t1.start();
t2.start();
}
}
class T1 extends Thread {
public void run(){
for(int i=0;i<5;i++) {
System.out.println("你是谁啊");
}
}
}
class T2 extends Thread {
public void run(){
for(int i=0;i<5;i++) {
System.out.println("我是线程");
}
}
}
运行结果
需要注意:线程并不是连续且顺序执行的,哪个线程执行主要看CPU给每个线程分配到的时间
2.实现Runnable接口
public class 实现Runnable {
public static void main(String[] args){
//创建一个线程要执行的任务
T t=new T();
//创建一个线程
Thread thread=new Thread(t);
//启动线程
thread.start();
}
}
class T implements Runnable{
@Override
public void run() {
System.out.println(1111111);
}
}
运行结果:
3.实现Callable接口
public class 实现Callable {
public static void main(String[] args){
CallableTest callableTest=new CallableTest();
FutureTask<String> futureTask=new FutureTask(callableTest);
futureTask.run();
try {
String st=futureTask.get();
System.out.println(st+"====");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class CallableTest implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("任务开始了");
Thread.sleep(1000);
return null;
}
}
运行结果:
这种创建线程方式需借助FutureTask类来实现
与实现Runnable接口的方式相比,可以在方法上抛出异常且有返回值
4.也可以通过匿名内部类的方式来创建线程
public class 匿名内部类的方式创建线程 {
public static void main(String[] args){
Thread thread=new Thread(){
public void run(){
System.out.println("我是线程");
}
};
thread.start();
}
}
这种方式等同于继承Thread
二、线程睡眠
Thread.sleep()方法可以令线程阻塞一段时间
public class 线程睡眠 {
public static void main(String[] args){
Thread thread=new Thread(){
public void run(){
Scanner sc=new Scanner(System.in);
int f=sc.nextInt();
while(f>=0){
System.out.println(f);
f--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread.start();
}
}
三、线程并发安全问题
两个或多个进程并发访问共享数据,共享数据的最终结果取决于进程运行的精确时序,这种情况称为竞争条件。为了防止竞争条件的发生,并发进程需要进行同步,也就是给共享资源安上一把锁(synchronized)
1.未加锁
public class 线程并发安全问题 {
static Object o=new Object();
public static void main(String[] args){
Thread t1=new Thread("小龙女"){
public void run(){
getBean(Thread.currentThread().getName());
}
};
Thread t2=new Thread("李莫愁"){
public void run(){
getBean(Thread.currentThread().getName());
}
};
t1.start();
t2.start();
}
public static void getBean(String name){
System.out.println(name+"进入商场");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+"挑衣服");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "进试衣间");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "试衣服");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "出试衣间");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+"走出商场");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
很明显,商场可以多个人一起进入,挑选衣服与离开商场也是可以同时进行,但当商场只有一个试衣间时,是不能两个人同时进入试衣间的,这时这个试衣间内的活动就是我们所说的共享资源,为了让多人使用这个共享资源不会发生冲突,我们需要给共享资源加上一把锁
2.加上锁
public class 线程并发安全问题 {
static Object o=new Object();
public static void main(String[] args){
Thread t1=new Thread("小龙女"){
public void run(){
getBean(Thread.currentThread().getName());
}
};
Thread t2=new Thread("李莫愁"){
public void run(){
getBean(Thread.currentThread().getName());
}
};
t1.start();
t2.start();
}
public static void getBean(String name){
System.out.println(name+"进入商场");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+"挑衣服");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (线程并发安全问题.class) {
System.out.println(name + "进试衣间");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "试衣服");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "出试衣间");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name+"走出商场");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果
当有线程在使用共享资源时,其他线程处于阻塞态,等待CPU给分配使用共享资源
四、加塞阻塞(插队)
当我们在打扫屋子时,我们需要先扫地,当地面扫干净后,我们才能拖地;而不能扫地和拖地一起进行或是先拖地后扫地,这时我们就需要做一个执行的顺序要求join()
public class 加塞阻塞or插队 {
public static void main(String[] args){
Thread t1=new Thread(){
public void run(){
for(int i=0;i<5;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我扫");
}
System.out.println("扫完了");
}
};
Thread t2=new Thread(){
public void run(){
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0;i<5;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我拖");
}
System.out.println("拖完了");
}
};
t1.start();
t2.start();
}
}
运行结果:
五、守护线程
拿一个小案例来说明
public class 守护线程 {
public static void main(String[] args){
Thread t1=new Thread(){
public void run(){
for(int i=0;i<5;i++){
System.out.println("i will jump!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("噗通。。。。。");
}
};
Thread thread2=new Thread(){
public void run(){
while(true){
System.out.println("you jump! i jump!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t1.start();
thread2.start();
}
}
在没有添加守护线程的条件下,thread2是不会受t1线程终止的影响而终止的
但是当我们将thread2设置为守护线程后,t1停止thread2就会停止
public class 守护线程 {
public static void main(String[] args){
Thread t1=new Thread(){
public void run(){
for(int i=0;i<5;i++){
System.out.println("i will jump!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("噗通。。。。。");
}
};
Thread thread2=new Thread(){
public void run(){
while(true){
System.out.println("you jump! i jump!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread2.setDaemon(true);
t1.start();
thread2.start();
}
}
其实java的public static void main(String[] args){} 就是一个守护线程
六、栅栏
当很多人约定好一个时间地点集合然后出发,我们需要保证当凑够五个人到了后才可以出发,这时我们可以设置一个栅栏(CyclicBarrier)
public class 栅栏CyclicBarrier {
public static void main(String[] args){
CyclicBarrier cb=new CyclicBarrier(5);
for(int i=0;i<14;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread t=new Thread(){
public void run(){
System.out.println("栅栏等待");
try {
cb.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("出发");
}
};
t.start();
}
}
}
运行结果:
因为一共就只有14个人,前十个人都可以正常出发,最后四个人因为凑不够五个人需要一直等待,线程处于阻塞状态
七、闭锁
因为线程是靠CPU分配时间来执行的,我们并不知道CPU会给线程分配到什么时间,但有时我们必须要求一个线程任务在某一时段执行,这时我们可以将所需线程加上一把锁(CountDownLatch)只有执行了所要求的线程数量后,才可以执行这个线程任务
public class 闭锁CountDownLatch {
public static void main(String[] args){
CountDownLatch cdl=new CountDownLatch(3);
Thread t1=new Thread(){
public void run(){
System.out.println("准备发射。。。。。");
cdl.countDown();
}
};
Thread t2=new Thread(){
public void run(){
System.out.println("检查冷却装置。。。。。");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("冷却装置检查完毕");
cdl.countDown();
}
};
Thread t3=new Thread(){
public void run(){
System.out.println("检查氧气装置。。。。。");
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("氧气装置检查完毕");
cdl.countDown();
}
};
Thread thread=new Thread(){
public void run(){
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("点火发射");
}
};
t1.start();
t2.start();
t3.start();
thread.start();
}
}
我们将锁加在了点火发射的线程任务上
运行结果:
八、睡眠阻断
可以通过interrupt()方法,将处于睡眠状态的线程阻断其睡眠,令其继续执行
public class 睡眠阻断 {
public static void main(String[] args){
Thread thread1=new Thread(){
public void run(){
try {
Thread.sleep(2000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("睡美人说 哎呀妈呀 破了相了");
}
};
Thread thread2=new Thread(){
public void run(){
for(int i=0;i<10;i++){
System.out.println("八十");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("墙破了");
thread1.interrupt();
}
};
thread1.start();
thread2.start();
}
}
九、volatile 共享变量
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰后,那么就具备了两层语义:
1、保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的
2、禁止进行指令重排序