上一篇:并发编程(4)—— 线程的生命周期
在不同的操作系统中,设置线程优先级是一个不稳定的,因为有的操作系统会忽略调优先级的设置,使用方式: thread.setPriority(int newPriority);
守护线程和主线程是共死的,主线程退出,守护线程肯定会退出,守护线程一般做的是支持行工作,例如内存方面的回收,对资源的清理;
测试代码:
public class TestDaemo {
public static void main(String[] args) {
Thread thread = new DaemonThread();
thread.setName("DaemonThread");
thread.start();
}
public static class DaemonThread extends Thread{
@Override
public void run(){
int a = 1;
while (true){
a++;
System.out.println(Thread.currentThread().getName() + a);
}
}
}
}
运行结果:
DaemonThread2
DaemonThread3
DaemonThread4
DaemonThread5
DaemonThread6
DaemonThread7
DaemonThread8
DaemonThread9
.
.
.
.
没有任何操作的的情况下线程会一直运行下去。
如果我们把它设置为守护线程:
public class TestDaemo {
public static void main(String[] args) {
Thread thread = new DaemonThread();
thread.setName("DaemonThread");
thread.setDaemon(true);
thread.start();
System.out.println("end");
}
public static class DaemonThread extends Thread{
@Override
public void run(){
int a = 1;
while (true){
a++;
System.out.println(Thread.currentThread().getName() + a);
}
}
}
}
运行结果:
子线程还没来的及运行主线程就运行完了,所以子线程也不会继续运行。
在守护线程中,finally代码块也不一定会执行
在设置守护线程的时候一定要在start()方法之前;
synchronized详解
代码示例(模拟快递发送通知):
快递实体类:
public class Express {
public static final String city = "上海市";
private int km;
private String site;
public Express() {
}
public Express(int km, String site) {
this.km = km;
this.site = site;
}
public synchronized void setSite(String site) {
this.site = site;
notifyAll();
}
public synchronized void checkKm(Integer km){
this.km = km;
notifyAll();
}
public synchronized void waitKm(){
while (this.km < 100){
try {
System.out.println("当前公里数是"+this.km+"小于100 继续等待!!!");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("当前公里数是"+this.km+"大于100 进行通知!!!");
}
public synchronized void waitCity(){
while (site.equals(this.city)){
try {
System.out.println("还没有出"+this.city+",继续等待");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("已到达"+this.site);
}
}
测试类:
public class TestThread extends Thread {
private static Express express = new Express(0,"上海市");
//检测公里数
public static class CheckKm extends Thread{
@Override
public void run(){
express.waitKm();
}
}
//检测到了哪哥城市
public static class ChectCity extends Thread{
@Override
public void run(){
express.waitCity();
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 3; i++){
Thread checkKm = new CheckKm();
checkKm.start();
}
for (int i = 0; i < 3; i++){
Thread ChectCity = new ChectCity();
ChectCity.start();
}
express.checkKm(10);
Thread.sleep(3000L);
express.checkKm(90);
Thread.sleep(3000L);
express.checkKm(122);
Thread.sleep(3000L);
express.setSite("北京");
}
}
运行结果:
当前公里数是10小于100 继续等待!!!
当前公里数是10小于100 继续等待!!!
当前公里数是10小于100 继续等待!!!
还没有出上海市,继续等待
还没有出上海市,继续等待
还没有出上海市,继续等待
还没有出上海市,继续等待
还没有出上海市,继续等待
还没有出上海市,继续等待
当前公里数是90小于100 继续等待!!!
当前公里数是90小于100 继续等待!!!
当前公里数是90小于100 继续等待!!!
当前公里数是122大于100 进行通知!!!
当前公里数是122大于100 进行通知!!!
当前公里数是122大于100 进行通知!!!
还没有出上海市,继续等待
还没有出上海市,继续等待
还没有出上海市,继续等待
已到达北京
已到达北京
已到达北京
Process finished with exit code 0
模拟数据库连接池获取连接和放回连接
SqlConnetion.java数据库连接
public class SqlConnetion implements Connection {
/*拿一个数据库连接*/
public static final Connection fetchConnection(){
return new SqlConnetion();
}
@Override
public Statement createStatement() throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return null;
}
@Override
public CallableStatement prepareCall(String sql) throws SQLException {
return null;
}
@Override
public String nativeSQL(String sql) throws SQLException {
return null;
}
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
}
@Override
public boolean getAutoCommit() throws SQLException {
return false;
}
@Override
public void commit() throws SQLException {
SleepTools.ms(100);
}
@Override
public void rollback() throws SQLException {
}
@Override
public void close() throws SQLException {
}
@Override
public boolean isClosed() throws SQLException {
return false;
}
@Override
public DatabaseMetaData getMetaData() throws SQLException {
return null;
}
@Override
public void setReadOnly(boolean readOnly) throws SQLException {
}
@Override
public boolean isReadOnly() throws SQLException {
return false;
}
@Override
public void setCatalog(String catalog) throws SQLException {
}
@Override
public String getCatalog() throws SQLException {
return null;
}
@Override
public void setTransactionIsolation(int level) throws SQLException {
}
@Override
public int getTransactionIsolation() throws SQLException {
return 0;
}
@Override
public SQLWarning getWarnings() throws SQLException {
return null;
}
@Override
public void clearWarnings() throws SQLException {
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
}
@Override
public Map> getTypeMap() throws SQLException {
return null;
}
@Override
public void setTypeMap(Map> map) throws SQLException {
}
@Override
public void setHoldability(int holdability) throws SQLException {
}
@Override
public int getHoldability() throws SQLException {
return 0;
}
@Override
public Savepoint setSavepoint() throws SQLException {
return null;
}
@Override
public Savepoint setSavepoint(String name) throws SQLException {
return null;
}
@Override
public void rollback(Savepoint savepoint) throws SQLException {
}
@Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return null;
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
return null;
}
@Override
public Clob createClob() throws SQLException {
return null;
}
@Override
public Blob createBlob() throws SQLException {
return null;
}
@Override
public NClob createNClob() throws SQLException {
return null;
}
@Override
public SQLXML createSQLXML() throws SQLException {
return null;
}
@Override
public boolean isValid(int timeout) throws SQLException {
return false;
}
@Override
public void setClientInfo(String name, String value) throws SQLClientInfoException {
}
@Override
public void setClientInfo(Properties properties) throws SQLClientInfoException {
}
@Override
public String getClientInfo(String name) throws SQLException {
return null;
}
@Override
public Properties getClientInfo() throws SQLException {
return null;
}
@Override
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
return null;
}
@Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
return null;
}
@Override
public void setSchema(String schema) throws SQLException {
}
@Override
public String getSchema() throws SQLException {
return null;
}
@Override
public void abort(Executor executor) throws SQLException {
}
@Override
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
}
@Override
public int getNetworkTimeout() throws SQLException {
return 0;
}
@Override
public T unwrap(Class iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class> iface) throws SQLException {
return false;
}
}
DBPool.java 模拟数据库连接池
public class DBPool {
//模拟连接池
private static LinkedList pool = new LinkedList<>();
//初始化链连接池
public DBPool(int initsize){
if(initsize > 0){
for (int i = 0; i < initsize; i++) {
pool.addLast(SqlConnetion.fetchConnection());
}
}
}
//从连接池获取连接
public Connection getConnection(long time) throws InterruptedException {
synchronized (pool){
if(time <= 0){
while (pool.isEmpty()){
pool.wait();
}
return pool.removeFirst();
}else{
long overTime = System.currentTimeMillis() + time;//等待最终时间
long retime = time;//需要等待多久
while (pool.isEmpty() && retime > 0){
pool.wait(retime);
retime = overTime - System.currentTimeMillis();//计算剩余等待时间
}
Connection result = null;
if(!pool.isEmpty()){
result = pool.removeFirst();
}
return result;
}
}
}
//归还连接到连接池
public void releaseConnection(Connection conn){
if(conn != null){
synchronized (pool){
pool.addLast(conn);
pool.notifyAll();
}
}
}
}
DBPoolTest.java 从连接池中获取连接
public class DBPoolTest {
//初始化连接池
static DBPool pool = new DBPool(10);
private static CountDownLatch countDownLatch;
public static void main(String[] args) throws InterruptedException {
int threadCount = 50;//启动50个线程
int workCount = 20;//每个线程操作20次
//获取到连接的次数
AtomicInteger get = new AtomicInteger(0);
//未获取到的次数
AtomicInteger notGet = new AtomicInteger(0);
countDownLatch = new CountDownLatch(threadCount);
for (int i = 0; i< threadCount; i++){
Thread thread = new Thread(new Worker(workCount,get,notGet),"线程"+i);
thread.start();
}
countDownLatch.await();
System.out.println("成功获取"+get.get()+"个链接");
System.out.println("失败获取"+notGet.get()+"个链接");
}
static class Worker implements Runnable{
private int count;
private AtomicInteger get;
private AtomicInteger notGet;
public Worker(int count, AtomicInteger get, AtomicInteger notGet){
this.count = count;
this.get = get;
this.notGet = notGet;
}
@Override
public void run() {
while (count > 0){
try {
//获取连接
Connection connection = pool.fetchConn(1000);
if(connection != null){
try {
connection.createStatement();//模拟操作数据库
connection.commit();//模拟操作数据库
} catch (SQLException e) {
e.printStackTrace();
}finally {
pool.releaseConn(connection);//归还连接
get.incrementAndGet();//记录得到连接的次数
}
}else{
notGet.incrementAndGet();//记录为得到连接的次数
System.out.println(Thread.currentThread().getName()+"等待超时");
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
count --;
}
}
countDownLatch.countDown();
}
}
}
运行结果:
线程19等待超时
线程32等待超时
.
.
.
.
线程29等待超时
成功获取890个链接
失败获取110个链接
Process finished with exit code 0
每次运行结果都不一样,大家可自己尝试
wait和notify/notifyAll的原理
调用wait方法,首先会获取监视器锁,获得成功以后,会让当前线程进入等待状态进入等待队列并且释放锁。
当其他线程调用notify后,会选择从等待队列中唤醒任意一个线程,而执行完notify方法以后,并不会立马唤醒线程,原因是当前的线程仍然持有这把锁,处于等待状态的线程无法获得锁。必须要等到当前的线程执行完按monitorexit指令以后,也就是锁被释放以后,处于等待队列中的线程就可以开始竞争锁了。
wait和notify/notifyAll为什么需要在synchronized里面?
wait方法的语义有两个,一个是释放当前的对象锁、另一个是使得当前线程进入阻塞队列,而这些操作都和监视器是相关的,所以wait必须要获得一个监视器锁。
而对于notify来说也是一样,它是唤醒一个线程,既然要去唤醒,首先得知道它在哪里,所以就必须要找到这个对象获取到这个对象的锁,然后到这个对象的等待队列中去唤醒一个线程。
yield()的作用是让步。它能让当前线程由“运行状态”进入到“就绪状态”。
yield()方法就是当前线程把占有的cpu资源让出来,然后同优先级的所有线程再去争抢这个cpu资源,可能会有同优先级的其他线程获取到cpu资源,也可能还是这个线程获取到cpu资源。
yield()不会释放锁,只是让出了cup的资源,让出了执行权
例:有A、B两个人做抢答题,一开始A抢到了答题的机会,但是A没有去答题,而是提出在重新进行一次抢答,这时就相当于A让出了答题的机会,然后A
、B又开始去抢答题的机会。
测试代码:
public class TestYeild {
public static int a = 1;
public static Object object = new Object();
public static class Answer extends Thread {
@Override
public void run() {
synchronized (object){
for(int i = 0; i< 5; i++){
System.out.println(Thread.currentThread().getName()+"获取到答题机会");
Thread.yield();
}
System.out.println("随便打印点啥------------------");
}
}
}
public static class Answer2 extends Thread {
@Override
public void run() {
synchronized (object){
System.out.println(Thread.currentThread().getName()+"获取到答题机会");
}
}
}
public static void main(String[] args) {
for (int i = 0; i< 5; i++){
Thread a = new Answer();
a.setName("A");
Thread b = new Answer2();
b.setName("B");a.start();
b.start();
}
}
}
运行结果:
一、加锁的运行结果
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
随便打印点啥------------------
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
随便打印点啥------------------
B获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
随便打印点啥------------------
B获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
随便打印点啥-----------------
B获取到答题机会
B获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
随便打印点啥------------------
B获取到答题机会
Process finished with exit code 0
二、不加锁(去掉synchronized关建字)运行结果:
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
B获取到答题机会
随便打印点啥------------------
A获取到答题机会
B获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
B获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
B获取到答题机会
随便打印点啥------------------
随便打印点啥------------------
B获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
A获取到答题机会
随便打印点啥------------------
随便打印点啥------------------
Process finished with exit code 0
join()方法使调用该方法的线程在此之前执行完毕,也就是等待该方法的线程执行完毕后再往下继续执行。注意该方法也需要捕捉异常
示例代码:
public class TestJoin {
public static class JoinThread extends Thread{
private Thread thread;
public JoinThread(Thread thread){this.thread = thread;};
@Override
public void run(){
for (int i = 0; i < 2; i++){
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("这是 "+Thread.currentThread().getName()+"线程");
}
}
}
public static class UserThread extends Thread{
@Override
public void run(){
for (int a = 0; a< 5; a++)
System.out.println("这是 UserThread 线程");
}
}
public static void main(String[] args) {
Thread t1 = new UserThread();
Thread join = new JoinThread(t1);
join.setName("join ");
join.start();
t1.start();
}
}
运行结果:
这是 UserThread 线程
这是 UserThread 线程
这是 UserThread 线程
这是 UserThread 线程
这是 UserThread 线程
这是 join 线程
这是 join 线程
Process finished with exit code 0
代码中可以看到,在join线程中调用的user线程的join方法,虽然我们先join.start()
但是还是等user线程运行完之后再运行的join线程