啥是线程
线程是程序执行的最小单位,一个进程可以有多个线程
线程都有哪些状态
创建(new)------>运行(runable)------->等待(wating)------->阻塞(blocked)------>结束(terminated)
创建:创建后尚未启动的线程处于的状态
运行:runable包括了线程状态的running和ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待cpu分配执行时间。
等待:处于这种状态的线程不会被分配cpu执行时间。等待状态又被分为无限期等待和有限期等待。处于无限期等待的线程需要被其他线程显示地唤醒(没有设置tiemout参数的Object.wait()、没有设置timeout的Thread.join()方法都会进入无限期等待);有限期等待状态无需被其他线程显示的唤醒,在一定时间后它们会由系统自动唤醒(Thread.sleep(),这是了timeout参数的Object.wait()、设置了timeout的Thread.join()方法都会进入有限期等待)
结束:已终止线程的线程状态,线程已经结束执行
如何创建线程
1-继承Thread类
public class TestThread{
public static void main(String[] args){
MyThread myThread = new MyThread();
myThread.start();
}
}
class MyThread extends Thread{
@Override
public void run(){
-----方法体
}
}
2-实现Runnable接口
public class TestThread{
public static void main(String[] args){
Thread thread = new Thread(new MyThread());
thread.start();
}
}
class MyThread implements Runnable{
@Override
public void run(){
----方法体
}
}
3-实现Callable接口
public class CallableTest {
public static void main(String[] args) {
//执行Callable 方式,需要FutureTask 实现实现,用于接收运算结果
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyCallable());
new Thread(futureTask).start();
//接收线程运算后的结果
try {
Integer sum = futureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
return sum;
}
}
4-线程池
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
//创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
ThreadPool threadPool = new ThreadPool();
for(int i =0;i<5;i++){
//为线程池分配任务
executorService.submit(threadPool);
}
//关闭线程池
executorService.shutdown();
}
}
class ThreadPool implements Runnable {
@Override
public void run() {
for(int i = 0 ;i<10;i++){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
啥是线程同步
线程同步的真实意思其实和字面意思是恰恰相反的。线程同步的真实意思是“排队”:几个线程制键要排队,一个一个对共享资源进行操作,而不是同时进行操作。
因此关于线程同步,其实就是线程排队。同步就是排队。线程同步的目的就是为了避免线程“同步”执行。
同步:我做完你再做。当一个线程访问该资源时,不允许其他线程访问
异步:我做我的你做你的。当一个线程可以同时访问一个资源,可能会发生线程安全问题。
为啥要线程同步
由于多个线程可能同时操作同一个数据,就好比买票,多个人可能会同时争抢一张票的情况,如果不进行线程同步,那么就会出现线程安全的问题。
线程同步的方法
线程有4中同步方法,分别为wait()、sleep()、notify()和notifyAll()。
wait():使线程处于一种等待状态,释放所持有的对象锁。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用它时要捕获InterruptedException异常,不释放对象锁。
notify():唤醒一个正在等待状态的线程。注意调用此方法时,并不能确切知道唤醒的是哪一个等待状态的线程,是由JVM来决定唤醒哪个线程,不是由线程优先级决定的。
notifyAll():唤醒所有等待状态的线程,注意并不是给所有唤醒线程一个对象锁,而是让它们竞争。
如何才能线程同步
前面已经说了为什么要线程同步,下面我们来研究一下怎么才能实现线程同步。
当我们出现多个线程同时访问一个共享资源的时候,我们可以给共享资源加一个锁,这把锁只有一把钥匙。哪个线程获得了这把钥匙,才有权力访问该共享资源。这个锁就是线程同步锁。此时我们会想到我们该把锁加在什么位置?
当然是加在共享资源上了。这可能是我们脑海中首先冒出的想法。没错,如果有可能,我们当然尽量把同步锁加在共享资源上,一些比较完善的共享资源,比如文件系统,数据库系统等,自身都提供了比较完善的同步锁机制。我们不用另外给这些资源加锁,这些资源自己就有锁。
所以我们应该将锁加在访问共享资源的代码片段上。其次我们要考虑加一个什么样的锁。
这里我们需要注意的是:访问同一个共享资源的不同代码片段,应该加上同一个同步锁;如果加的是不同的同步锁,那么便没有任何意义。因此同步锁本身也一定是多个线程之间的共享对象。
synchronized同步锁
synchronized关键字,它可以保证在同一时刻最多只有一个线程执行该段代码。
synchronized有三种应用方式:
1-普通同步方法(实例方法),锁是当前实例对象,进入同步代码块前要获得当前实例的锁。
public class synchronizedTest implements Runnable {
//共享资源
static int i =0;
/**
* synchronized 修饰实例方法
*/
public synchronized void increase(){
i++;
}
@Override
public void run(){
for (int j =0 ; j<10000;j++){
increase();
}
}
public static void main(String[] args) throws InterruptedException {
synchronizedTest test = new synchronizedTest();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
2-静态同步方法,锁是当前类的class对象,进入同步代码前要获得当前类对象的锁
public class synchronizedTest implements Runnable {
//共享资源
static int i =0;
/**
* synchronized 修饰实例方法
*/
public static synchronized void increase(){
i++;
}
@Override
public void run(){
for (int j =0 ; j<10000;j++){
increase();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new synchronizedTest());
Thread t2 = new Thread(new synchronizedTest());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
3-同步方法块,琐是括号里面的对象,对给定对象加锁,进入同步代码块前要获得给定对象的锁
public class synchronizedTest implements Runnable {
static synchronizedTest instance=new synchronizedTest();
static int i=0;
@Override
public void run() {
//省略其他耗时操作....
//使用同步代码块对变量i进行同步操作,锁对象为instance
synchronized(instance){
for(int j=0;j<10000;j++){
i++;
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(instance);
Thread t2=new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}