1)是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法
2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。
3)synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/区块/},它的作用域是当前对象;
4) synchronized(Foo.class) 它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。
5)synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。
同步代码块
synchronized(对象){ }
synchronized(类.class){ }
同步函数
public synchronized void sale(){}
public static synchronized void sale(){}
wait():导致当前线程等待并使其进入到等待阻塞状态。直到其他线程调用该同步锁对象的notify()或notifyAll()方法来唤醒此线程。会释放所持有的对象的锁
public final void wait() throws InterruptedException
public final void wait(long timeout) throws InterruptedException
//指定等待时长,毫秒为单位
public final void wait(long timeout,int nanos) throws InterruptedException
//指定等待时长,毫秒+纳秒,timeout毫秒为单位,nanos纳秒为单位。时间精度更高
notify():唤醒在此同步锁对象上等待的单个线程,如果有多个线程都在此同步锁对象上等待,则会任意选择其中某个线程进行唤醒操作,只有当前线程放弃对同步锁对象的锁定,才可能执行被唤醒的线程。
public final void notify()
notifyAll():唤醒在此同步锁对象上等待的所有线程,只有当前线程放弃对同步锁对象的锁定,才可能执行被唤醒的线程。
public final void notify()
在调用同一个对象的wait()方法和notify()方法的语句必须放在同步代码块中,并且同步代码块使用该对象的同步锁,否则在运行时则会抛出IllegalMonitorStateException异常
示例
class ThreadA {
static Object o1 = new Object();//可以是任意一个对象,或者自定义的对象
public static void main(String[] args) {
ThreadB b = new ThreadB();
b.start();
System.out.println("b is start....");
synchronized (o1)// 主线程获取o1的对象锁
{
try {
System.out.println("Waiting for b to complete...");
o1.wait();//o1的对象锁释放,主线程进入等待状态
System.out.println("Completed.Now back to main thread");
} catch (InterruptedException e) {
}
}
System.out.println("Total is :" + b.total);
}
}
class ThreadB extends Thread {
int total;
public void run() {
synchronized (o1) {//ThreadB获取o1的对象锁
System.out.println("ThreadB is running..");
for (int i = 0; i < 5; i++) {
total += i;
System.out.println("total is " + total);
}
o1.notify();//ThreadB释放o1的对象锁,通知其他等待o1对象锁的线程继续运行
}
}
}
o1.wait()没设置时间,需要o1.notify(),主线程才能继续运行
输出有以下两种情况:
b is start....
ThreadB is running..
total is 0
total is 1
total is 3
total is 6
total is 10
Waiting for b to complete...
b is start....
Waiting for b to complete...
ThreadB is running..
total is 0
total is 1
total is 3
total is 6
total is 10
Completed.Now back to main thread
Total is :10
sleep()是Thread类的静态方法。该方法声明抛出了InterrupedException异常。所以使用时,要么捕捉,要么声明抛出。有两种重载方式:
static void sleep(long millis); //让当前正在执行的线程暂停millis毫秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度和准度的影响。
static void sleep(long millis , int nanos); //让当前正在执行的线程暂停millis毫秒加nanos微秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的
精度和准度的影响。
sleep() 的作用是让当前线程休眠,即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。sleep()会指定休眠时间,线程休眠的时间会大于/等于该休眠时间;在线程重新被唤醒时,它会由“阻塞状态”变成“就绪状态”,从而等待cpu的调度执行。常用来暂停程序的运行。同时注意,sleep()方法不会释放锁。
yield()是Thread类的静态方法。它能让当前线程暂停,但不会阻塞该线程,而是由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行。因此,**使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。**但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权,**也有可能是当前线程又进入到“运行状态”继续运行!**值得注意的是,yield()方法不会释放锁。
package com.demo.test;
public class ThreadYield extends Thread{
public ThreadYield(String name) {
super(name);
}
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println("" + this.getName() + "-----" + i);
// 当i为30时,该线程就会把CPU时间让掉,让其他或者自己的线程执行(也就是谁先抢到谁执行)
if (i ==30) {
this.yield();
}
}
}
}
package com.demo.test;
public class Main {
public static void main(String[] args) {
ThreadYield yt1 = new ThreadYield("A");
ThreadYield yt2 = new ThreadYield("B");
yt1.start();
yt2.start();
}
}
第一种情况:A线程当执行到30时会将CPU时间让掉,这时B线程抢到CPU时间并执行。
第二种情况:A线程当执行到30时会将CPU时间让掉,这时A线程抢到CPU时间并执行。
第三种情况:B线程当执行到30时会将CPU时间让掉,这时A线程抢到CPU时间并执行。
第四种情况:B线程当执行到30时会将CPU时间让掉,这时B线程抢到CPU时间并执行。
也就是说:谁抢到就是谁的。
1 . 优先级表示重要程度或者紧急程度.但是能不能抢到资源也是不一定.
2 . 分配优先级:反映线程的重要或紧急程度
线程的优先级用1~10 表示,1的优先级最低,10的优先级最高,默认值是5
/**
* 优先级 : 只能反映 线程 的 中或者是 紧急程度 , 不能决定 是否一定先执行
* setPriority()
* 1~10 1最低 10最高 5是默认值
*/
public class Test {
public static void main(String[] args) {
MyThread thread = new MyThread("二狗");
thread.setPriority(1);
MyThread thread2 = new MyThread("小香菇");
thread2.setPriority(10);
MyThread thread3 = new MyThread("小蘑菇");
MyThread thread4 = new MyThread("观海同志");
thread4.setPriority(3);
thread.start();
thread2.start();
thread3.start();
thread4.start();
}
}
class MyThread extends Thread{
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
通常我们会有这样的需求,即停止一个线程。在java的api中有stop、suspend等方法可以达到目的,但由于这些方法在使用上存在不安全性,会带来不好的副作用,不建议被使用。
中断在java中主要有3个方法,interrupt(),isInterrupted()和interrupted()。
*interrupt(),在一个线程中调用另一个线程的interrupt()方法,即会向那个线程发出信号——线程中断状态已被设置。至于那个线程何去何从,由具体的代码实现决定。
*isInterrupted(),用来判断当前线程的中断状态(true or false)。
*interrupted()是个Thread的static方法,用来恢复中断状态
如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,此时调用该线程的interrupt()方法,那么该线程将抛出一个 InterruptedException中断异常(该线程必须事先预备好处理此异常),从而提早地终结被阻塞状态。如果线程没有被阻塞,这时调用 interrupt()将不起作用,直到执行到wait(),sleep(),join()时,才马上会抛出 InterruptedException。
import java.util.Date;
public class TestInterrupted implements Runnable {
public void run(){
System.out.println("开始运行run方法,时间:"+(new Date()));
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("线程已被唤醒!");
}
System.out.println("结束运行run方法,时间:"+(new Date()));
}
}
public class TestMainInterrupted {
public static void main(String[] args) {
TestInterrupted t = new TestInterrupted();
Thread th = new Thread(t);
th.start();
try {
th.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
th.interrupt();
}
}
运行结果:
开始运行run方法,时间:Sat Dec 14 19:18:29 CST 2019
线程已被唤醒!
结束运行run方法,时间:Sat Dec 14 19:18:31 CST 2019
线程的唤醒是指线程从休眠的阻塞状态转变为运行状态,可以通过Thread类的interrupt()方法实现。
该线程th启动后即进入休眠,原本需要休眠5000ms,但是主线程启动后,两秒就将其唤醒。并且,将休眠的饿线程唤醒后会抛出异常,即输出catch语句块的内容。
1.即刻停止run()方法中剩余的全部工作,包括在catch或finally语句中,并抛出ThreadDeath异常(通常情况下此异常不需要显示的捕获),因此可能会导致一些清理性的工作的得不到完成,如文件,数据库等的关闭。
2.会立即释放该线程所持有的所有的锁,导致数据得不到同步的处理,出现数据不一致的问题。
public class Main{
public static void main(String [] args) throws Exception{
TestObject testObject = new TestObject();
Thread t1 = new Thread(){
public void run(){
try {
testObject.print("1", "2");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t1.start();
Thread.sleep(1000);
t1.stop();
System.out.println("first : " + testObject.getFirst() + " " + "second : " + testObject.getSecond());
}
}
class TestObject{
private String first = "ja";
private String second = "va";
public synchronized void print(String first, String second) throws Exception{
this.first = first;
Thread.sleep(10000);
this.second = second;
}
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
public String getSecond() {
return second;
}
public void setSecond(String second) {
this.second = second;
}
}
输出:
first : 1 second : va
从上面的程序验证结果来看,stop()确实是不安全的。它的不安全主要是针对于第二点:释放该线程所持有的所有的锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用thread.stop()后导致了该线程所持有的所有锁的突然释放,那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。
因为suspend方法并不会释放锁,如果使用suspend的目标线程对一个重要的系统资源持有锁,那么没任何线程可以使用这个资源直到要suspend的目标线程被resumed,如果一个线程在resume目标线程之前尝试持有这个重要的系统资源锁再去resume目标线程,这两条线程就相互死锁了,也就冻结线程。
如果 resume() 操作出现在 suspend() 之前执行,那么线程将一直处于挂起状态,同时一直占用锁,这就产生了死锁。而且,对于被挂起的线程,它的线程状态居然还是 Runnable。
public class Main{
public static void main(String [] args) throws Exception{
TestObject testObject = new TestObject();
Thread t1 = new Thread(){
public void run(){
testObject.print();
}
};
t1.setName("A");
t1.start();
Thread.sleep(1000);
Thread t2 = new Thread(){
public void run(){
System.out.println("B已启动,但进入不到print方法中");
testObject.print();
}
};
t2.setName("B");
t2.start();
}
}
class TestObject{
public synchronized void print(){
if(Thread.currentThread().getName().equals("A")){
System.out.println("A 线程 独占该资源了");
Thread.currentThread().suspend();
}
}
}
输出:
A 线程 独占该资源了
已启动,但进入不到print方法中