线程
新建线程
- 继承Thread类:
public class newThead{
private static class UseThread extends Thread{
@Override
public void run{
System.out.printIn("I'm created by extends Thread")
}
}
public static void main(String[] args){
new UseThread().start();
}
}
注: Java是单继承的语言,如果继承了Thred 类的话,就不能继承其他类了。
- 实现Runable接口
public class newThread{
private static class UseRunnable implements Runnable{
@Override
public void run(){
System.out.printIn("I'm created by implements Runable")
}
}
public static void main(Sting[] args){
new Thread(UseRunable).satrt();
}
}
注: run方法没有返回值。
- 实现Callable接口
public class newThread{
private static class UseCall implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("I am implements Callable");
return "CallResult";
}
}
public static void main(String[] args){
UseCall useCall = new UseCall();
FutureTask<String> futureTask = new FutureTask<>(useCall);
new Thread(futureTask).start();
System.out.println(futureTask.get());
}
}
结束线程
线程自然结束有两种情况
-
线程执行完成,自然结束
-
抛出未处理异常
不建议使用stop(),stop()会导致线程不能正确的释放资源,不建议使用suspend(),容易导致死锁
interrupt() 、 isInterrupted()、static方法**interrupted()**的关系与区别
-
interrupt() 中断线程,并不是强行关闭这个线程,而是将线程的中断标志位设为 true ,线程是否中断由线程自己决定。
-
isInterrupted() 判定当前线程是否处于中断状态。
-
static方法interrupted() 判定当前线程是否处于中断状态,同时中断标志位改为false
public class EndThread {
private static class UseThread extends Thread{
public UseThread(String name) {
super(name);
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
while(!Thread.currentThread().isInterrupted()) {
System.out.println(threadName+" is run!");
}
System.out.println(threadName+" interrput flag is "+isInterrupted());
}
}
public static void main(String[] args) throws InterruptedException {
Thread endThread = new UseThread("endThread");
endThread.start();
Thread.sleep(20);
endThread.interrupt();
}
}
输出打印结果为
endThread is run!
endThread interrput flag is true
说明我们在调用 endThread.interrupt(); 之后 endThread的中断标志编程true了;
在抛出异常的时候,会自动将线程的中断标志重置为false,案例如下
public class HasInterrputException {
private static SimpleDateFormat formater
= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss_SSS");
private static class UseThread extends Thread{
public UseThread(String name) {
super(name);
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
while(!isInterrupted()) {
try {
System.out.println("UseThread:"+formater.format(new Date()));
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println(threadName+" catch interrput flag is "
+isInterrupted()+ " at "
+(formater.format(new Date())));
interrupt();
e.printStackTrace();
}
System.out.println(threadName);
}
System.out.println(threadName+" interrput flag is "
+isInterrupted());
}
}
public static void main(String[] args) throws InterruptedException {
Thread endThread = new UseThread("HasInterrputEx");
endThread.start();
System.out.println("Main:"+formater.format(new Date()));
Thread.sleep(800);
System.out.println("Main begin interrupt thread:"+formater.format(new Date()));
endThread.interrupt();
}
输出结果为:
UseThread:2020-04-01 17:06:49_682
Main:2020-04-01 17:06:49_682
Main begin interrupt thread:2020-04-01 17:06:50_502
HasInterrputEx catch interrput flag is false at 2020-04-01 17:06:50_502
java.lang.InterruptedException: sleep interrupted
at java.base/java.lang.Thread.sleep(Native Method)
at com.xiangxue.ch1.safeend.HasInterrputException$UseThread.run(HasInterrputException.java:28)
HasInterrputEx
HasInterrputEx interrput flag is true
Process finished with exit code 0
线程的状态
线程的状态如下图所示,线程在不同的状态之间切换。
线程中start与run方法的区别
run()方法只是普通对象里面普通的方法,只有调用了start(),之后JVM会将线程对象和操作系统里面的实际线程进行映射,再执行run方法。
yild()
让出cpu的执行权,将线程从「运行状态」转换为「可运行状态」 ,该线程已让有可能被再次选中。
线程的优先级
取值为1-10,缺省为5,但是线程的优先级不可靠,不建议作为线程开发时候的手段。
守护线程
和主线程共死,finally 不能保证一定执行。
public class DaemonThread {
private static class UseThread extends Thread {
@Override
public void run() {
try {
while (!isInterrupted()) {
System.out.println(Thread.currentThread().getName()
+ " I am extends Thread.");
}
System.out.println(Thread.currentThread().getName()
+ " interrupt flag is " + isInterrupted());
} finally {
System.out.println("...........finally");
}
}
}
public static void main(String[] args) throws InterruptedException,
ExecutionException {
UseThread useThread = new UseThread();
useThread.setDaemon(true);
useThread.start();
Thread.sleep(5);
useThread.interrupt();
}
}
线程之间的共享
synchronized
-
对象锁,锁的是对象的实例,代码如下
public class SynInstance { private static class InstanceSyn implements Runnable{ private SynInstance synInstance; public InstanceSyn(SynInstance synInstance){ this.synInstance = synInstance; } @Override public void run() { System.out.println("TestInstance is running ... "+ synInstance); synInstance.instance(); } } private static class Instance2Syn implements Runnable{ private SynInstance synInstance; public Instance2Syn(SynInstance synInstance) { this.synInstance = synInstance; } @Override public void run() { System.out.println("TestInstance is running ......"+synInstance); synInstance.instance2(); } } private synchronized void instance(){ SleepTools.second(3); System.out.println("synInstance is going ...."+this.toString()); SleepTools.second(3); System.out.println("synInstance end "+ this.toString()); } private synchronized void instance2(){ SleepTools.second(3); System.out.println("synInstance2 is going ...."+this.toString()); SleepTools.second(3); System.out.println("synInstance2 end "+ this.toString()); } public static void main (String[] args){ SynInstance synInstance = new SynInstance(); Thread t1 = new Thread(new InstanceSyn(synInstance)); // Thread t2 = new Thread(new Instance2Syn(new SynInstance())); 1⃣️ // Thread t2 = new Thread(new Instance2Syn(synInstance)); 2⃣️ t1.start(); t2.start(); SleepTools.second(1); } }若运行1⃣️的话结果如下
TestInstance is running ......com.sakura.ch1.syn.SynInstance@10d9dd76 TestInstance is running ... com.sakura.ch1.syn.SynInstance@1374ac79 synInstance is going ....com.sakura.ch1.syn.SynInstance@1374ac79 synInstance2 is going ....com.sakura.ch1.syn.SynInstance@10d9dd76 synInstance end com.sakura.ch1.syn.SynInstance@1374ac79 synInstance2 end com.sakura.ch1.syn.SynInstance@10d9dd76若运2⃣️的话,结果如下
TestInstance is running ... com.sakura.ch1.syn.SynInstance@29046243 TestInstance is running ......com.sakura.ch1.syn.SynInstance@29046243 synInstance is going ....com.sakura.ch1.syn.SynInstance@29046243 synInstance end com.sakura.ch1.syn.SynInstance@29046243 synInstance2 is going ....com.sakura.ch1.syn.SynInstance@29046243 synInstance2 end com.sakura.ch1.syn.SynInstance@29046243 -
类锁 锁的是每个类的Class对象,每个类的Class对象在虚拟机中只有一个,所以类锁只有一个,代码如下
public class SynClass { private static class SynCl extends Thread{ @Override public void run() { System.out.println("TestClass is running ....."); synCl(); } } private static synchronized void synCl(){ SleepTools.second(1); System.out.printf("synClass going...."); SleepTools.second(1); System.out.println("synClass end"); } public static void main(String[] args){ SynCl synClass = new SynCl(); synClass.start(); SynCl synCl = new SynCl(); synCl.start(); SleepTools.second(1); } }输出结果为
TestClass is running ..... TestClass is running ..... synClass going....synClass end synClass going....synClass end
Volatile
JVM提供的最轻量的同步机制,适合于只有一个线程写,多个线程读的场景,因为它只能确保可见性。并不是线程安全的,
public class VolaUnSafe {
private static class VolatileVar implements Runnable{
private volatile int a = 0;
@Override
public void run() {
String threadName = Thread.currentThread().getName();
a = a++;
System.out.println(threadName+"-----"+a);
a = a+1;
System.out.println(threadName+"-----"+a);
}
}
public static void main(String[] args){
VolatileVar v = new VolatileVar();
Thread t1 = new Thread(v);
Thread t2 = new Thread(v);
Thread t3 = new Thread(v);
Thread t4 = new Thread(v);
Thread t5 = new Thread(v);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
输出结果会混乱如下
Thread-0-----0
Thread-4-----0
Thread-0-----1
Thread-1-----0
Thread-2-----0
Thread-3-----0
Thread-3-----5
Thread-2-----4
Thread-1-----3
Thread-4-----2
ThreadLocal
线程内部的变量,ThreadLocal类里面有个ThreadLocalMap<Thread,Integer>,他的键为Thread对象,值为该变量在该线程中的值,运用代码如下
public class UseThreadLocal {
static final ThreadLocal<Integer> a = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 1;
}
};
private static class TestThread implements Runnable{
int id;
public TestThread(int id) {
this.id = id;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" start1");
Integer s = a.get();
s = s + id;
a.set(s);
System.out.println(Thread.currentThread().getName()+":"+a.get());
}
}
private void startThreeThread(){
Thread[] runs = new Thread[3];
for(int i = 0;i< runs.length;i++){
runs[i] = new Thread(new TestThread(i));
}
for(int j = 0;j<runs.length;j++){
runs[j].start();
}
}
public static void main(String[] args){
UseThreadLocal useThreadLocal = new UseThreadLocal();
useThreadLocal.startThreeThread();
}
}
输出结果为
Thread-0 start1
Thread-2 start1
Thread-1 start1
Thread-1:2
Thread-2:3
Thread-0:1
线程之间的协作
wait() notify()/notifyAll()
这三个都是对象上的方法,wait()阻塞当前线程,交出执行权,释放锁。notify()是唤醒某个wait()的线程,notifyAll(),是唤醒所有的wait()线程。notify和notifyAll应该用谁?
应该尽量使用notifyAll,使用notify因为有可能发生信号丢失的的情况
等待通知的标准范式
等待方:
1、 获取对象的锁;
2、 循环里判断条件是否满足,不满足调用wait方法,
3、 条件满足执行业务逻辑
通知方来说
1、 获取对象的锁;
2、 改变条件
3、 通知所有等待在对象的线程
具体使用如下代码:
Express.class 快递邮件对象
public class Express {
public static final String CITY="shanghai";
private int km;
private String site;
public Express(int km, String site) {
this.km = km;
this.site = site;
}
public synchronized void changeKm(){
this.km = 101;
notify();//1⃣️
//notifyAll() 2⃣️
}
public synchronized void changeSite(){
this.site = "beijing";
notify ();//1⃣️
//notifyAll() 2⃣️
}
public synchronized void waitKm(){
while (km <= 100){
try {
wait();
System.out.println("checkKM Thread ["+Thread.currentThread().getId()+" ] is be notified");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("the km is "+this.km+",i will change DB");
}
public synchronized void waitSite(){
while (CITY.equals(this.site)){
try {
wait();
System.out.println("checkSite Thread ["+Thread.currentThread().getId()+" ] is be notified");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("the site is "+this.km+",i will call user");
}
}
调用
public class TestWN {
private static Express express = new Express(100,Express.CITY);
private static class CheckKm extends Thread{
@Override
public void run() {
express.waitKm();
}
}
private static class CheckSite extends Thread{
@Override
public void run() {
express.waitSite();
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0;i < 3;i++){
new CheckSite().start();
}
for (int i = 0;i < 3;i++){
new CheckKm().start();
}
Thread.sleep(1000);
express.changeKm();
}
}
运行1⃣️输出如下
checkSite Thread [11 ] is be notified
运行2⃣️输出如下
checkKM Thread [16 ] is be notified
the km is 101,i will change DB
checkKM Thread [15 ] is be notified
the km is 101,i will change DB
checkKM Thread [14 ] is be notified
the km is 101,i will change DB
checkSite Thread [13 ] is be notified
checkSite Thread [12 ] is be notified
checkSite Thread [11 ] is be notified
等待超时模式实现一个线程池
实现一个Connection,SqlConnectionImpl
public class SqlConnectionImpl implements Connection {
/*拿一个数据库连接*/
public static final Connection fetchConnection(){
return new SqlConnectImpl();
}
@Override
public Statement createStatement() throws SQLException {
return null;
}
...
}
先写一个超时模式的连接池
public class DBPool {
private LinkedList<Connection> pool = new LinkedList<>();
public DBPool(int initialSize) {
if(initialSize > 0){
for (int i = 0;i<initialSize;i++){
pool.add(SqlConnectImpl.fetchConnection());
}
}
}
public Connection fetchCon(long mills) throws InterruptedException {
synchronized (pool){
if(mills<0){
while (pool.isEmpty()){
pool.wait();
}
return pool.removeFirst();
}else{
long overtime = System.currentTimeMillis()+mills;
long remain = mills;
while (pool.isEmpty()&&remain>0){
pool.wait(remain);
remain = overtime - System.currentTimeMillis();
}
Connection result = null;
if(!pool.isEmpty()){
result = pool.removeFirst();
}
return result;
}
}
}
public void releaseConn(Connection con){
if(null!=con){
synchronized (pool){
pool.addLast(con);
pool.notifyAll();
}
}
}
}
然后定义一个测试
public class DBPoolTest {
static DBPool dbPool = new DBPool(10);
static CountDownLatch end;
public static void main(String[] args ) throws InterruptedException {
int threadCount = 50;
end = new CountDownLatch(threadCount);
int count = 20;
AtomicInteger got = new AtomicInteger();
AtomicInteger notGot = new AtomicInteger();
for (int i = 0;i<threadCount;i++){
Thread thread = new Thread(new Worker(count, got, notGot), "work_" + i);
thread.start();
}
end.await();
System.out.println("总共尝试了: " + (threadCount * count));
System.out.println("拿到连接的次数: " + got);
System.out.println("没能连接的次数: " + notGot);
}
private static class Worker implements Runnable{
int count;
AtomicInteger got;
AtomicInteger notGot;
public Worker(int count, AtomicInteger got, AtomicInteger notGot) {
this.count = count;
this.got = got;
this.notGot = notGot;
}
@Override
public void run() {
while (count >0){
try {
Connection connection = dbPool.fetchCon(1000);
if(null!=connection){
try{
connection.createStatement();
connection.commit();
}finally {
dbPool.releaseConn(connection);
got.incrementAndGet();
}
}else{
notGot.incrementAndGet();
System.out.println(Thread.currentThread().getName()+"等待超时");
}
} catch (Exception e) {
}finally {
count --;
}
}
end.countDown();
}
}
}
输出结果如下:
work_30等待超时
work_21等待超时
work_26等待超时
...
work_6等待超时
work_21等待超时
work_20等待超时
work_40等待超时
总共尝试了: 1000
拿到连接的次数: 868
没能连接的次数: 132
join()
join()方法,线程A,执行了线程B的join(),线程A必须要等待B执行完成了以后,线程A才能继续自己的工作,可以用于嵌套定义线程的执行顺序,如下
public class UseJoin {
private static class JumpQueue implements Runnable{
private Thread thread;
public JumpQueue(Thread thread) {
this.thread = thread;
}
@Override
public void run() {
try {
System.out.println(thread.getName()+"will join before "+Thread.currentThread().getName());
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" terminted.");
}
}
public static void main(String[] args){
Thread previous = Thread.currentThread();
for (int i = 0;i<10;i++){
Thread thread = new Thread(new JumpQueue(previous), "work_" + i);
System.out.println(previous.getName()+"will jump a queue the thread :"+thread.getName());
thread.start();
previous = thread;
}
SleepTools.second(2);
System.out.println(Thread.currentThread().getName()+" terminted.");
}
}
输出结果如下
mainwill jump a queue the thread :work_0
work_0will jump a queue the thread :work_1
mainwill join before work_0
work_1will jump a queue the thread :work_2
work_0will join before work_1
work_2will jump a queue the thread :work_3
work_1will join before work_2
work_3will jump a queue the thread :work_4
work_2will join before work_3
work_4will jump a queue the thread :work_5
work_3will join before work_4
work_5will jump a queue the thread :work_6
work_4will join before work_5
work_6will jump a queue the thread :work_7
work_5will join before work_6
work_7will jump a queue the thread :work_8
work_6will join before work_7
work_8will jump a queue the thread :work_9
work_7will join before work_8
work_8will join before work_9
main terminted.
work_0 terminted.
work_1 terminted.
work_2 terminted.
work_3 terminted.
work_4 terminted.
work_5 terminted.
work_6 terminted.
work_7 terminted.
work_8 terminted.
work_9 terminted.
调用yield() 、sleep()、wait()、notify()等方法对锁有何影响?
-
线程在执行yield()以后,持有的锁是不释放的
-
sleep()方法被调用以后,持有的锁是不释放的,好的编码风格不要吧sleep(),写在同步块里面
public class SleepLock { private Object lock = new Object(); private class ThreadSleep extends Thread { @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name + "will take the lock"); try { synchronized (lock) { System.out.println(name + " taking the lock "); Thread.sleep(5000); System.out.println(name + " finish the word "); } } catch (InterruptedException e) { e.printStackTrace(); } } } private class ThreadNotSleep extends Thread{ @Override public void run() { String name = Thread.currentThread().getName(); System.out.println(name+"will take the lock "+System.currentTimeMillis()); synchronized (lock){ System.out.println(name+" taking the lock "); System.out.println(name+" finish the work "+System.currentTimeMillis()); } } } public static void main(String[] args){ SleepLock sleepLock = new SleepLock(); Thread threadSleep = sleepLock.new ThreadSleep(); threadSleep.setName("ThreadSleep"); ThreadNotSleep threadNotSleep = sleepLock.new ThreadNotSleep(); threadNotSleep.setName("ThreadNotSleep"); threadSleep.start(); try { Thread.sleep(1000); System.out.println("main slept"); } catch (InterruptedException e) { e.printStackTrace(); } threadNotSleep.start(); } }输出
ThreadSleepwill take the lock ThreadSleep taking the lock main slept ThreadNotSleepwill take the lock 1586845673063 ThreadSleep finish the word ThreadNotSleep taking the lock ThreadNotSleep finish the work 1586845677063 -
调动wait()方法之前,必须要持有锁。调用了wait()方法以后,锁就会被释放,当wait方法返回的时候,线程会重新持有锁
-
调动notify()方法之前,必须要持有锁,调用notify()方法本身不会释放锁的